mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-19 10:19:27 -05:00
Support excluding tags
This commit is contained in:
parent
ddf386c4d4
commit
29a7737829
@ -19,7 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div *ngIf="selectionModel.items" class="items">
|
<div *ngIf="selectionModel.items" class="items">
|
||||||
<ng-container *ngFor="let item of selectionModel.itemsSorted | filter: filterText">
|
<ng-container *ngFor="let item of selectionModel.itemsSorted | filter: filterText">
|
||||||
<app-toggleable-dropdown-button *ngIf="allowSelectNone || item.id" [item]="item" [state]="selectionModel.get(item.id)" (toggle)="selectionModel.toggle(item.id)"></app-toggleable-dropdown-button>
|
<app-toggleable-dropdown-button *ngIf="allowSelectNone || item.id" [item]="item" [state]="selectionModel.get(item.id)" (toggle)="selectionModel.toggle(item.id)" (exclude)="selectionModel.exclude(item.id)"></app-toggleable-dropdown-button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<button *ngIf="editing" class="list-group-item list-group-item-action bg-light" (click)="applyClicked()" [disabled]="!selectionModel.isDirty()">
|
<button *ngIf="editing" class="list-group-item list-group-item-action bg-light" (click)="applyClicked()" [disabled]="!selectionModel.isDirty()">
|
||||||
|
@ -43,6 +43,10 @@ export class FilterableDropdownSelectionModel {
|
|||||||
return this.items.filter(i => this.temporarySelectionStates.get(i.id) == ToggleableItemState.Selected)
|
return this.items.filter(i => this.temporarySelectionStates.get(i.id) == ToggleableItemState.Selected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getExcludedItems() {
|
||||||
|
return this.items.filter(i => this.temporarySelectionStates.get(i.id) == ToggleableItemState.Excluded)
|
||||||
|
}
|
||||||
|
|
||||||
set(id: number, state: ToggleableItemState, fireEvent = true) {
|
set(id: number, state: ToggleableItemState, fireEvent = true) {
|
||||||
if (state == ToggleableItemState.NotSelected) {
|
if (state == ToggleableItemState.NotSelected) {
|
||||||
this.temporarySelectionStates.delete(id)
|
this.temporarySelectionStates.delete(id)
|
||||||
@ -56,9 +60,9 @@ export class FilterableDropdownSelectionModel {
|
|||||||
|
|
||||||
toggle(id: number, fireEvent = true) {
|
toggle(id: number, fireEvent = true) {
|
||||||
let state = this.temporarySelectionStates.get(id)
|
let state = this.temporarySelectionStates.get(id)
|
||||||
if (state == null || state != ToggleableItemState.Selected) {
|
if (state == null || (state != ToggleableItemState.Selected && state != ToggleableItemState.Excluded)) {
|
||||||
this.temporarySelectionStates.set(id, ToggleableItemState.Selected)
|
this.temporarySelectionStates.set(id, ToggleableItemState.Selected)
|
||||||
} else if (state == ToggleableItemState.Selected) {
|
} else if (state == ToggleableItemState.Selected || state == ToggleableItemState.Excluded) {
|
||||||
this.temporarySelectionStates.delete(id)
|
this.temporarySelectionStates.delete(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +90,29 @@ export class FilterableDropdownSelectionModel {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exclude(id: number, fireEvent:boolean = true) {
|
||||||
|
console.log('exclude', id, fireEvent);
|
||||||
|
|
||||||
|
let state = this.temporarySelectionStates.get(id)
|
||||||
|
if (state == null || state != ToggleableItemState.Excluded) {
|
||||||
|
this.temporarySelectionStates.set(id, ToggleableItemState.Excluded)
|
||||||
|
} else if (state == ToggleableItemState.Excluded) {
|
||||||
|
this.temporarySelectionStates.delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.multiple) {
|
||||||
|
for (let key of this.temporarySelectionStates.keys()) {
|
||||||
|
if (key != id) {
|
||||||
|
this.temporarySelectionStates.delete(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fireEvent) {
|
||||||
|
this.changed.next(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private getNonTemporary(id: number) {
|
private getNonTemporary(id: number) {
|
||||||
return this.selectionStates.get(id) || ToggleableItemState.NotSelected
|
return this.selectionStates.get(id) || ToggleableItemState.NotSelected
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<button class="list-group-item list-group-item-action d-flex align-items-center p-2 border-top-0 border-left-0 border-right-0 border-bottom" role="menuitem" (click)="toggleItem()">
|
<button class="list-group-item list-group-item-action d-flex align-items-center p-2 border-top-0 border-left-0 border-right-0 border-bottom" role="menuitem" (click)="toggleItem($event)">
|
||||||
<div class="selected-icon mr-1">
|
<div class="selected-icon mr-1">
|
||||||
<ng-container *ngIf="isChecked()">
|
<ng-container *ngIf="isChecked()">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-check" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-check" viewBox="0 0 16 16">
|
||||||
@ -10,7 +10,11 @@
|
|||||||
<path d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8z"/>
|
<path d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<ng-container *ngIf="isExcluded()">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-x" viewBox="0 0 16 16">
|
||||||
|
<path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
|
||||||
|
</svg>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<div class="mr-1">
|
<div class="mr-1">
|
||||||
<app-tag *ngIf="isTag; else displayName" [tag]="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>
|
||||||
|
@ -10,7 +10,8 @@ export interface ToggleableItem {
|
|||||||
export enum ToggleableItemState {
|
export enum ToggleableItemState {
|
||||||
NotSelected = 0,
|
NotSelected = 0,
|
||||||
Selected = 1,
|
Selected = 1,
|
||||||
PartiallySelected = 2
|
PartiallySelected = 2,
|
||||||
|
Excluded = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -32,12 +33,19 @@ export class ToggleableDropdownButtonComponent {
|
|||||||
@Output()
|
@Output()
|
||||||
toggle = new EventEmitter()
|
toggle = new EventEmitter()
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
exclude = new EventEmitter()
|
||||||
|
|
||||||
get isTag(): boolean {
|
get isTag(): boolean {
|
||||||
return 'is_inbox_tag' in this.item
|
return 'is_inbox_tag' in this.item
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleItem(): void {
|
toggleItem(event: MouseEvent): void {
|
||||||
this.toggle.emit()
|
if (event.altKey) {
|
||||||
|
this.exclude.emit()
|
||||||
|
} else {
|
||||||
|
this.toggle.emit()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isChecked() {
|
isChecked() {
|
||||||
@ -48,4 +56,7 @@ export class ToggleableDropdownButtonComponent {
|
|||||||
return this.state == ToggleableItemState.PartiallySelected
|
return this.state == ToggleableItemState.PartiallySelected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isExcluded() {
|
||||||
|
return this.state == ToggleableItemState.Excluded
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ 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_ANY_TAG, FILTER_HAS_TAG, 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_ANY_TAG, FILTER_HAS_TAG, FILTER_DOES_NOT_HAVE_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';
|
import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component';
|
||||||
|
|
||||||
@ -107,6 +107,9 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
|||||||
case FILTER_HAS_ANY_TAG:
|
case FILTER_HAS_ANY_TAG:
|
||||||
this.tagSelectionModel.set(null, ToggleableItemState.Selected, false)
|
this.tagSelectionModel.set(null, ToggleableItemState.Selected, false)
|
||||||
break
|
break
|
||||||
|
case FILTER_DOES_NOT_HAVE_TAG:
|
||||||
|
this.tagSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Excluded, false)
|
||||||
|
break
|
||||||
case FILTER_CORRESPONDENT:
|
case FILTER_CORRESPONDENT:
|
||||||
this.correspondentSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false)
|
this.correspondentSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false)
|
||||||
break
|
break
|
||||||
@ -128,6 +131,9 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
|||||||
this.tagSelectionModel.getSelectedItems().filter(tag => tag.id).forEach(tag => {
|
this.tagSelectionModel.getSelectedItems().filter(tag => tag.id).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.tagSelectionModel.getExcludedItems().filter(tag => tag.id).forEach(tag => {
|
||||||
|
filterRules.push({rule_type: FILTER_DOES_NOT_HAVE_TAG, value: tag.id?.toString()})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
this.correspondentSelectionModel.getSelectedItems().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()})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user