mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-30 18:27:45 -05:00
Merge remote-tracking branch 'upstream/dev' into feature/popover-previews
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<div class="card mb-3 shadow-sm" [class.card-selected]="selected" [class.document-card]="selectable" [class.popover-hidden]="popoverHidden" (mouseleave)="mouseLeaveCard()">
|
||||
<div class="row no-gutters">
|
||||
<div class="col-md-2 d-none d-lg-block doc-img-background rounded-left" [class.doc-img-background-selected]="selected" (click)="this.toggleSelected.emit($event)">
|
||||
<img [src]="getThumbUrl()" class="card-img doc-img border-right rounded-left">
|
||||
<img [src]="getThumbUrl()" class="card-img doc-img border-right rounded-left" [class.inverted]="getIsThumbInverted()">
|
||||
|
||||
<div style="top: 0; left: 0" class="position-absolute border-right border-bottom bg-light p-1" [class.document-card-check]="!selected">
|
||||
<div class="custom-control custom-checkbox">
|
||||
|
@@ -3,6 +3,7 @@ import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
|
||||
import { PaperlessDocumentMetadata } from 'src/app/data/paperless-document-metadata';
|
||||
import { DocumentService } from 'src/app/services/rest/document.service';
|
||||
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
|
||||
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
@Component({
|
||||
@@ -12,7 +13,7 @@ import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
|
||||
})
|
||||
export class DocumentCardLargeComponent implements OnInit {
|
||||
|
||||
constructor(private documentService: DocumentService, private sanitizer: DomSanitizer) { }
|
||||
constructor(private documentService: DocumentService, private sanitizer: DomSanitizer, private settingsService: SettingsService) { }
|
||||
|
||||
@Input()
|
||||
selected = false
|
||||
@@ -65,6 +66,10 @@ export class DocumentCardLargeComponent implements OnInit {
|
||||
})
|
||||
}
|
||||
|
||||
getIsThumbInverted() {
|
||||
return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)
|
||||
}
|
||||
|
||||
getDetailsAsString() {
|
||||
if (typeof this.details === 'string') {
|
||||
return this.details.substring(0, 500)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<div class="col p-2 h-100">
|
||||
<div class="card h-100 shadow-sm document-card" [class.card-selected]="selected" [class.popover-hidden]="popoverHidden" (mouseleave)="mouseLeaveCard()">
|
||||
<div class="border-bottom doc-img-container" [class.doc-img-background-selected]="selected" (click)="this.toggleSelected.emit($event)">
|
||||
<img class="card-img doc-img rounded-top" [src]="getThumbUrl()">
|
||||
<img class="card-img doc-img rounded-top" [class.inverted]="getIsThumbInverted()" [src]="getThumbUrl()">
|
||||
|
||||
<div class="border-right border-bottom bg-light p-1 rounded document-card-check">
|
||||
<div class="custom-control custom-checkbox">
|
||||
|
@@ -3,6 +3,7 @@ import { map } from 'rxjs/operators';
|
||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
|
||||
import { PaperlessDocumentMetadata } from 'src/app/data/paperless-document-metadata';
|
||||
import { DocumentService } from 'src/app/services/rest/document.service';
|
||||
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
|
||||
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
@Component({
|
||||
@@ -12,7 +13,7 @@ import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
|
||||
})
|
||||
export class DocumentCardSmallComponent implements OnInit {
|
||||
|
||||
constructor(private documentService: DocumentService) { }
|
||||
constructor(private documentService: DocumentService, private settingsService: SettingsService) { }
|
||||
|
||||
@Input()
|
||||
selected = false
|
||||
@@ -44,6 +45,10 @@ export class DocumentCardSmallComponent implements OnInit {
|
||||
})
|
||||
}
|
||||
|
||||
getIsThumbInverted() {
|
||||
return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)
|
||||
}
|
||||
|
||||
getThumbUrl() {
|
||||
return this.documentService.getThumbUrl(this.document.id)
|
||||
}
|
||||
|
@@ -2,7 +2,15 @@
|
||||
<div class="col mb-2 mb-xl-0">
|
||||
<div class="form-inline d-flex align-items-center">
|
||||
<label class="text-muted mr-2 mb-0" i18n>Filter by:</label>
|
||||
<input class="form-control form-control-sm flex-fill w-auto" type="text" [(ngModel)]="titleFilter" placeholder="Title" i18n-placeholder>
|
||||
<div class="input-group input-group-sm flex-fill w-auto">
|
||||
<div class="input-group-prepend" ngbDropdown>
|
||||
<button class="btn btn-outline-primary" ngbDropdownToggle>{{textFilterTargetName}}</button>
|
||||
<div class="dropdown-menu shadow" ngbDropdownMenu>
|
||||
<button *ngFor="let t of textFilterTargets" ngbDropdownItem [class.active]="textFilterTarget == t.id" (click)="changeTextFilterTarget(t.id)">{{t.name}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<input class="form-control form-control-sm" type="text" [(ngModel)]="textFilter">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-100 d-xl-none"></div>
|
||||
|
@@ -8,10 +8,13 @@ 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_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_TITLE, FILTER_TITLE_CONTENT } 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';
|
||||
|
||||
const TEXT_FILTER_TARGET_TITLE = "title"
|
||||
const TEXT_FILTER_TARGET_TITLE_CONTENT = "title-content"
|
||||
|
||||
@Component({
|
||||
selector: 'app-filter-editor',
|
||||
templateUrl: './filter-editor.component.html',
|
||||
@@ -64,7 +67,19 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
||||
correspondents: PaperlessCorrespondent[] = []
|
||||
documentTypes: PaperlessDocumentType[] = []
|
||||
|
||||
_titleFilter = ""
|
||||
_textFilter = ""
|
||||
|
||||
textFilterTargets = [
|
||||
{id: TEXT_FILTER_TARGET_TITLE, name: $localize`Title`},
|
||||
{id: TEXT_FILTER_TARGET_TITLE_CONTENT, name: $localize`Title & content`}
|
||||
]
|
||||
|
||||
textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT
|
||||
|
||||
get textFilterTargetName() {
|
||||
return this.textFilterTargets.find(t => t.id == this.textFilterTarget)?.name
|
||||
}
|
||||
|
||||
|
||||
tagSelectionModel = new FilterableDropdownSelectionModel()
|
||||
correspondentSelectionModel = new FilterableDropdownSelectionModel()
|
||||
@@ -80,7 +95,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
||||
this.documentTypeSelectionModel.clear(false)
|
||||
this.tagSelectionModel.clear(false)
|
||||
this.correspondentSelectionModel.clear(false)
|
||||
this._titleFilter = null
|
||||
this._textFilter = null
|
||||
this.dateAddedBefore = null
|
||||
this.dateAddedAfter = null
|
||||
this.dateCreatedBefore = null
|
||||
@@ -89,7 +104,12 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
||||
value.forEach(rule => {
|
||||
switch (rule.rule_type) {
|
||||
case FILTER_TITLE:
|
||||
this._titleFilter = rule.value
|
||||
this._textFilter = rule.value
|
||||
this.textFilterTarget = TEXT_FILTER_TARGET_TITLE
|
||||
break
|
||||
case FILTER_TITLE_CONTENT:
|
||||
this._textFilter = rule.value
|
||||
this.textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT
|
||||
break
|
||||
case FILTER_CREATED_AFTER:
|
||||
this.dateCreatedAfter = rule.value
|
||||
@@ -121,8 +141,11 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
||||
|
||||
get filterRules(): FilterRule[] {
|
||||
let filterRules: FilterRule[] = []
|
||||
if (this._titleFilter) {
|
||||
filterRules.push({rule_type: FILTER_TITLE, value: this._titleFilter})
|
||||
if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_TITLE_CONTENT) {
|
||||
filterRules.push({rule_type: FILTER_TITLE_CONTENT, value: this._textFilter})
|
||||
}
|
||||
if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_TITLE) {
|
||||
filterRules.push({rule_type: FILTER_TITLE, value: this._textFilter})
|
||||
}
|
||||
if (this.tagSelectionModel.isNoneSelected()) {
|
||||
filterRules.push({rule_type: FILTER_HAS_ANY_TAG, value: "false"})
|
||||
@@ -165,15 +188,15 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
||||
this.filterRulesChange.next(this.filterRules)
|
||||
}
|
||||
|
||||
get titleFilter() {
|
||||
return this._titleFilter
|
||||
get textFilter() {
|
||||
return this._textFilter
|
||||
}
|
||||
|
||||
set titleFilter(value) {
|
||||
this.titleFilterDebounce.next(value)
|
||||
set textFilter(value) {
|
||||
this.textFilterDebounce.next(value)
|
||||
}
|
||||
|
||||
titleFilterDebounce: Subject<string>
|
||||
textFilterDebounce: Subject<string>
|
||||
subscription: Subscription
|
||||
|
||||
ngOnInit() {
|
||||
@@ -181,19 +204,19 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
||||
this.correspondentService.listAll().subscribe(result => this.correspondents = result.results)
|
||||
this.documentTypeService.listAll().subscribe(result => this.documentTypes = result.results)
|
||||
|
||||
this.titleFilterDebounce = new Subject<string>()
|
||||
this.textFilterDebounce = new Subject<string>()
|
||||
|
||||
this.subscription = this.titleFilterDebounce.pipe(
|
||||
this.subscription = this.textFilterDebounce.pipe(
|
||||
debounceTime(400),
|
||||
distinctUntilChanged()
|
||||
).subscribe(title => {
|
||||
this._titleFilter = title
|
||||
).subscribe(text => {
|
||||
this._textFilter = text
|
||||
this.updateRules()
|
||||
})
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.titleFilterDebounce.complete()
|
||||
this.textFilterDebounce.complete()
|
||||
}
|
||||
|
||||
resetSelected() {
|
||||
@@ -223,4 +246,9 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
||||
onDocumentTypeDropdownOpen() {
|
||||
this.documentTypeSelectionModel.apply()
|
||||
}
|
||||
|
||||
changeTextFilterTarget(target) {
|
||||
this.textFilterTarget = target
|
||||
this.updateRules()
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user