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> |       </div> | ||||||
|       <div *ngIf="selectionModel.items" class="items"> |       <div *ngIf="selectionModel.items" class="items"> | ||||||
|         <ng-container *ngFor="let toggleableItem of selectionModel.items | filter: filterText"> |         <ng-container *ngFor="let item of selectionModel.items | filter: filterText"> | ||||||
|           <app-toggleable-dropdown-button [toggleableItem]="toggleableItem" (toggle)="selectionModel.toggle(toggleableItem.item)"></app-toggleable-dropdown-button> |           <app-toggleable-dropdown-button [item]="item" [state]="selectionModel.get(item.id)" (toggle)="selectionModel.toggle(item.id)"></app-toggleable-dropdown-button> | ||||||
|         </ng-container> |         </ng-container> | ||||||
|       </div> |       </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"> |       <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 { ToggleableItem, ToggleableItemState } from './toggleable-dropdown-button/toggleable-dropdown-button.component'; | ||||||
| import { MatchingModel } from 'src/app/data/matching-model'; | import { MatchingModel } from 'src/app/data/matching-model'; | ||||||
| import { Subject } from 'rxjs'; | import { Subject } from 'rxjs'; | ||||||
|  | import { ThrowStmt } from '@angular/compiler'; | ||||||
|  |  | ||||||
| export enum FilterableDropdownType { | export enum FilterableDropdownType { | ||||||
|   Filtering = 'filtering', |   Filtering = 'filtering', | ||||||
| @@ -16,39 +17,56 @@ export class FilterableDropdownSelectionModel { | |||||||
|  |  | ||||||
|   multiple = false |   multiple = false | ||||||
|  |  | ||||||
|   items: ToggleableItem[] = [] |   items: MatchingModel[] = [] | ||||||
|  |  | ||||||
|   getSelected() { |   selection = new Map<number, ToggleableItemState>() | ||||||
|     return this.items.filter(i => i.state == ToggleableItemState.Selected).map(i => i.item) |  | ||||||
|  |   getSelectedItems() { | ||||||
|  |     return this.items.filter(i => this.selection.get(i.id) == ToggleableItemState.Selected) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   set(id: number, state: ToggleableItemState, fireEvent = true) { | ||||||
|  |     this.selection.set(id, state) | ||||||
|   toggle(item: MatchingModel, fireEvent = true) { |     if (fireEvent) { | ||||||
|     console.log("TOGGLE TAG") |       this.changed.next(this) | ||||||
|     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) |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   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() { |   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() |   @Input() | ||||||
|   set items(items: MatchingModel[]) { |   set items(items: MatchingModel[]) { | ||||||
|     if (items) { |     if (items) { | ||||||
|       this._selectionModel.items = items.map(i => { |       this._selectionModel.items = items | ||||||
|         return {item: i, state: ToggleableItemState.NotSelected, count: i.document_count} |  | ||||||
|       }) |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   get items(): MatchingModel[] { |   get items(): MatchingModel[] { | ||||||
|     return this._selectionModel.items.map(i => i.item) |     return this._selectionModel.items | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   _selectionModel = new FilterableDropdownSelectionModel() |   _selectionModel = new FilterableDropdownSelectionModel() | ||||||
|   | |||||||
| @@ -5,8 +5,8 @@ | |||||||
|     </svg> |     </svg> | ||||||
|   </div> |   </div> | ||||||
|   <div class="mr-1"> |   <div class="mr-1"> | ||||||
|     <app-tag *ngIf="isTag; else displayName" [tag]="toggleableItem?.item" [clickable]="true" linkTitle="Filter by tag"></app-tag> |     <app-tag *ngIf="isTag; else displayName" [tag]="item" [clickable]="true" linkTitle="Filter by tag"></app-tag> | ||||||
|     <ng-template #displayName><small>{{toggleableItem?.item.name}}</small></ng-template> |     <ng-template #displayName><small>{{item.name}}</small></ng-template> | ||||||
|   </div> |   </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> | </button> | ||||||
|   | |||||||
| @@ -21,13 +21,19 @@ export enum ToggleableItemState { | |||||||
| export class ToggleableDropdownButtonComponent { | export class ToggleableDropdownButtonComponent { | ||||||
|  |  | ||||||
|   @Input() |   @Input() | ||||||
|   toggleableItem: ToggleableItem |   item: MatchingModel | ||||||
|  |  | ||||||
|  |   @Input() | ||||||
|  |   state: ToggleableItemState | ||||||
|  |  | ||||||
|  |   @Input() | ||||||
|  |   count: number | ||||||
|  |  | ||||||
|   @Output() |   @Output() | ||||||
|   toggle = new EventEmitter() |   toggle = new EventEmitter() | ||||||
|  |  | ||||||
|   get isTag(): boolean { |   get isTag(): boolean { | ||||||
|     return 'is_inbox_tag' in this.toggleableItem?.item // ~ this.item instanceof PaperlessTag |     return 'is_inbox_tag' in this.item | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   toggleItem(): void { |   toggleItem(): void { | ||||||
| @@ -35,9 +41,9 @@ export class ToggleableDropdownButtonComponent { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   getSelectedIconName() { |   getSelectedIconName() { | ||||||
|     if (this.toggleableItem?.state == ToggleableItemState.Selected) { |     if (this.state == ToggleableItemState.Selected) { | ||||||
|       return "check" |       return "check" | ||||||
|     } else if (this.toggleableItem?.state == ToggleableItemState.PartiallySelected) { |     } else if (this.state == ToggleableItemState.PartiallySelected) { | ||||||
|       return "dash" |       return "dash" | ||||||
|     } else { |     } else { | ||||||
|       return "" |       return "" | ||||||
|   | |||||||
| @@ -3,13 +3,14 @@ import { PaperlessTag } from 'src/app/data/paperless-tag'; | |||||||
| import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'; | import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'; | ||||||
| import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'; | import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'; | ||||||
| import { Subject, Subscription } from 'rxjs'; | 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 { DocumentTypeService } from 'src/app/services/rest/document-type.service'; | ||||||
| import { TagService } from 'src/app/services/rest/tag.service'; | import { TagService } from 'src/app/services/rest/tag.service'; | ||||||
| import { CorrespondentService } from 'src/app/services/rest/correspondent.service'; | import { CorrespondentService } from 'src/app/services/rest/correspondent.service'; | ||||||
| import { FilterRule } from 'src/app/data/filter-rule'; | 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 { FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component'; | ||||||
|  | import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'; | ||||||
|  |  | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-filter-editor', |   selector: 'app-filter-editor', | ||||||
| @@ -61,7 +62,6 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | |||||||
|  |  | ||||||
|   @Input() |   @Input() | ||||||
|   set filterRules (value: FilterRule[]) { |   set filterRules (value: FilterRule[]) { | ||||||
|     console.log("SET FILTER RULES") |  | ||||||
|     value.forEach(rule => { |     value.forEach(rule => { | ||||||
|       switch (rule.rule_type) { |       switch (rule.rule_type) { | ||||||
|         case FILTER_TITLE: |         case FILTER_TITLE: | ||||||
| @@ -79,31 +79,34 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | |||||||
|         case FILTER_ADDED_BEFORE: |         case FILTER_ADDED_BEFORE: | ||||||
|           this.dateAddedBefore = rule.value |           this.dateAddedBefore = rule.value | ||||||
|           break |           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() |   @Output() | ||||||
|   filterRulesChange = new EventEmitter<FilterRule[]>() |   filterRulesChange = new EventEmitter<FilterRule[]>() | ||||||
|  |  | ||||||
|   updateRules() { |   updateRules() { | ||||||
|     console.log("UPDATE RULES!!!") |  | ||||||
|     let filterRules: FilterRule[] = [] |     let filterRules: FilterRule[] = [] | ||||||
|     if (this._titleFilter) { |     if (this._titleFilter) { | ||||||
|       filterRules.push({rule_type: FILTER_TITLE, value: 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()}) |       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()}) |       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()}) |       filterRules.push({rule_type: FILTER_DOCUMENT_TYPE, value: documentType.id.toString()}) | ||||||
|     }) |     }) | ||||||
|     if (this.dateCreatedBefore) { |     if (this.dateCreatedBefore) { | ||||||
| @@ -118,13 +121,12 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | |||||||
|     if (this.dateAddedAfter) { |     if (this.dateAddedAfter) { | ||||||
|       filterRules.push({rule_type: FILTER_ADDED_AFTER, value: this.dateAddedAfter}) |       filterRules.push({rule_type: FILTER_ADDED_AFTER, value: this.dateAddedAfter}) | ||||||
|     } |     } | ||||||
|     console.log(filterRules) |  | ||||||
|     this.filterRulesChange.next(filterRules) |     this.filterRulesChange.next(filterRules) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   hasFilters() { |   hasFilters() { | ||||||
|     return this._titleFilter ||  |     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() |       this.tagSelectionModel.selectionSize() || this.correspondentSelectionModel.selectionSize() || this.documentTypeSelectionModel.selectionSize() | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -161,16 +163,26 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | |||||||
|  |  | ||||||
|   clearSelected() { |   clearSelected() { | ||||||
|     this._titleFilter = "" |     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() |     this.updateRules() | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   toggleTag(tagId: number) { |   toggleTag(tagId: number) { | ||||||
|  |     this.tagSelectionModel.toggle(tagId) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   toggleCorrespondent(correspondentId: number) { |   toggleCorrespondent(correspondentId: number) { | ||||||
|  |     this.correspondentSelectionModel.toggle(correspondentId) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   toggleDocumentType(documentTypeId: number) { |   toggleDocumentType(documentTypeId: number) { | ||||||
|  |     this.documentTypeSelectionModel.toggle(documentTypeId) | ||||||
|   } |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 jonaswinkler
					jonaswinkler