mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-02 13:45:10 -05:00
Refactor filterable dropdowns to allow intermediate state
This commit is contained in:
parent
01d448ecde
commit
275bd96ba8
@ -1,7 +1,7 @@
|
||||
<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()">
|
||||
<div class="selected-icon mr-1">
|
||||
<svg *ngIf="selected" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#check" />
|
||||
<svg *ngIf="getSelectedIconName() !== ''" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
|
||||
<use attr.xlink:href="assets/bootstrap-icons.svg#{{getSelectedIconName()}}" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mr-1">
|
||||
|
@ -2,6 +2,7 @@ import { Component, EventEmitter, Input, Output, OnInit } from '@angular/core';
|
||||
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 { SelectableItem, SelectableItemState } from '../filterable-dropdown.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-filterable-dropdown-button',
|
||||
@ -11,10 +12,11 @@ import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
|
||||
export class FilterableDropdownButtonComponent implements OnInit {
|
||||
|
||||
@Input()
|
||||
item: PaperlessTag | PaperlessDocumentType | PaperlessCorrespondent
|
||||
selectableItem: SelectableItem
|
||||
|
||||
@Input()
|
||||
selected: boolean
|
||||
get item(): PaperlessTag | PaperlessDocumentType | PaperlessCorrespondent {
|
||||
return this.selectableItem?.item
|
||||
}
|
||||
|
||||
@Output()
|
||||
toggle = new EventEmitter()
|
||||
@ -26,7 +28,14 @@ export class FilterableDropdownButtonComponent implements OnInit {
|
||||
}
|
||||
|
||||
toggleItem(): void {
|
||||
this.selected = !this.selected
|
||||
this.toggle.emit(this.item)
|
||||
this.selectableItem.state = (this.selectableItem.state == SelectableItemState.NotSelected || this.selectableItem.state == SelectableItemState.PartiallySelected) ? SelectableItemState.Selected : SelectableItemState.NotSelected
|
||||
this.toggle.emit(this.selectableItem)
|
||||
}
|
||||
|
||||
getSelectedIconName() {
|
||||
let iconName = ''
|
||||
if (this.selectableItem?.state == SelectableItemState.Selected) iconName = 'check'
|
||||
else if (this.selectableItem?.state == SelectableItemState.PartiallySelected) iconName = 'minus'
|
||||
return iconName
|
||||
}
|
||||
}
|
||||
|
@ -19,9 +19,9 @@
|
||||
<input class="form-control" type="text" [(ngModel)]="filterText" placeholder="Filter {{title}}" (keyup.enter)="listFilterEnter()" #listFilterTextInput>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="items" class="items">
|
||||
<ng-container *ngFor="let item of items | filter: filterText; let i = index">
|
||||
<app-filterable-dropdown-button [item]="item" [selected]="isItemSelected(item)" (toggle)="toggleItem($event)"></app-filterable-dropdown-button>
|
||||
<div *ngIf="selectableItems" class="items">
|
||||
<ng-container *ngFor="let selectableItem of selectableItems | filter: filterText">
|
||||
<app-filterable-dropdown-button [selectableItem]="selectableItem" (toggle)="toggleItem($event)"></app-filterable-dropdown-button>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,8 +1,22 @@
|
||||
import { Component, EventEmitter, Input, Output, ElementRef, ViewChild } from '@angular/core';
|
||||
import { ObjectWithId } from 'src/app/data/object-with-id';
|
||||
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 { FilterPipe } from 'src/app/pipes/filter.pipe';
|
||||
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
|
||||
|
||||
export interface SelectableItem {
|
||||
item: PaperlessTag | PaperlessDocumentType | PaperlessCorrespondent,
|
||||
state: SelectableItemState
|
||||
}
|
||||
|
||||
export enum SelectableItemState {
|
||||
NotSelected = 0,
|
||||
Selected = 1,
|
||||
PartiallySelected = 2
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-filterable-dropdown',
|
||||
templateUrl: './filterable-dropdown.component.html',
|
||||
@ -13,10 +27,26 @@ export class FilterableDropdownComponent {
|
||||
constructor(private filterPipe: FilterPipe) { }
|
||||
|
||||
@Input()
|
||||
items: ObjectWithId[]
|
||||
set items(items: ObjectWithId[]) {
|
||||
if (items) {
|
||||
this.selectableItems = items.map(i => {
|
||||
return {item: i, state: SelectableItemState.NotSelected}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
selectableItems: SelectableItem[] = []
|
||||
|
||||
@Input()
|
||||
itemsSelected: ObjectWithId[]
|
||||
set itemsSelected(itemsSelected: ObjectWithId[]) {
|
||||
this.selectableItems.forEach(i => {
|
||||
i.state = (itemsSelected.find(is => is.id == i.item.id)) ? SelectableItemState.Selected : SelectableItemState.NotSelected
|
||||
})
|
||||
}
|
||||
|
||||
get itemsSelected() :ObjectWithId[] {
|
||||
return this.selectableItems.filter(si => si.state == SelectableItemState.Selected).map(si => si.item)
|
||||
}
|
||||
|
||||
@Input()
|
||||
title: string
|
||||
@ -35,12 +65,8 @@ export class FilterableDropdownComponent {
|
||||
|
||||
filterText: string
|
||||
|
||||
toggleItem(item: ObjectWithId): void {
|
||||
this.toggle.emit(item)
|
||||
}
|
||||
|
||||
isItemSelected(item: ObjectWithId): boolean {
|
||||
return this.itemsSelected?.find(i => i.id == item.id) !== undefined
|
||||
toggleItem(selectableItem: SelectableItem): void {
|
||||
this.toggle.emit(selectableItem.item)
|
||||
}
|
||||
|
||||
dropdownOpenChange(open: boolean): void {
|
||||
@ -50,12 +76,12 @@ export class FilterableDropdownComponent {
|
||||
}, 0);
|
||||
} else {
|
||||
this.filterText = ''
|
||||
this.close.emit(this.itemsSelected)
|
||||
this.close.next()
|
||||
}
|
||||
}
|
||||
|
||||
listFilterEnter(): void {
|
||||
let filtered = this.filterPipe.transform(this.items, this.filterText)
|
||||
let filtered = this.filterPipe.transform(this.selectableItems, this.filterText)
|
||||
if (filtered.length == 1) this.toggleItem(filtered.shift())
|
||||
this.dropdown.close()
|
||||
}
|
||||
|
@ -1,16 +1,17 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { SelectableItem } from 'src/app/components/common/filterable-dropdown/filterable-dropdown.component';
|
||||
|
||||
@Pipe({
|
||||
name: 'filter'
|
||||
})
|
||||
export class FilterPipe implements PipeTransform {
|
||||
transform(items: any[], searchText: string): any[] {
|
||||
if (!items) return [];
|
||||
if (!searchText) return items;
|
||||
transform(selectableItems: SelectableItem[], searchText: string): any[] {
|
||||
if (!selectableItems) return [];
|
||||
if (!searchText) return selectableItems;
|
||||
|
||||
return items.filter(item => {
|
||||
return Object.keys(item).some(key => {
|
||||
return String(item[key]).toLowerCase().includes(searchText.toLowerCase());
|
||||
return selectableItems.filter(selectableItem => {
|
||||
return Object.keys(selectableItem.item).some(key => {
|
||||
return String(selectableItem.item[key]).toLowerCase().includes(searchText.toLowerCase());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user