mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-03-17 06:25:57 +00:00
Wire up selection with hasSelection, allSelected since we dont get list of IDs anymore
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
<div ngbDropdown class="btn-group flex-fill d-sm-none">
|
||||
<button class="btn btn-sm btn-outline-primary" id="dropdownSelectMobile" ngbDropdownToggle>
|
||||
<i-bs name="text-indent-left"></i-bs><div class="d-none d-sm-inline ms-1"><ng-container i18n>Select</ng-container></div>
|
||||
@if (list.selected.size > 0) {
|
||||
<pngx-clearable-badge [selected]="list.selected.size > 0" [number]="list.selected.size" (cleared)="list.selectNone()"></pngx-clearable-badge><span class="visually-hidden">selected</span>
|
||||
@if (list.hasSelection) {
|
||||
<pngx-clearable-badge [selected]="list.hasSelection" [number]="list.selectedCount" (cleared)="list.selectNone()"></pngx-clearable-badge><span class="visually-hidden">selected</span>
|
||||
}
|
||||
</button>
|
||||
<div ngbDropdownMenu aria-labelledby="dropdownSelectMobile" class="shadow">
|
||||
@@ -17,7 +17,7 @@
|
||||
<span class="input-group-text border-0" i18n>Select:</span>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm flex-nowrap">
|
||||
@if (list.selected.size > 0) {
|
||||
@if (list.hasSelection) {
|
||||
<button class="btn btn-sm btn-outline-secondary" (click)="list.selectNone()">
|
||||
<i-bs name="slash-circle" class="me-1"></i-bs><ng-container i18n>None</ng-container>
|
||||
</button>
|
||||
@@ -127,11 +127,11 @@
|
||||
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
|
||||
<ng-container i18n>Loading...</ng-container>
|
||||
}
|
||||
@if (list.selected.size > 0) {
|
||||
<span i18n>{list.collectionSize, plural, =1 {Selected {{list.selected.size}} of one document} other {Selected {{list.selected.size}} of {{list.collectionSize || 0}} documents}}</span>
|
||||
@if (list.hasSelection) {
|
||||
<span i18n>{list.collectionSize, plural, =1 {Selected {{list.selectedCount}} of one document} other {Selected {{list.selectedCount}} of {{list.collectionSize || 0}} documents}}</span>
|
||||
}
|
||||
@if (!list.isReloading) {
|
||||
@if (list.selected.size === 0) {
|
||||
@if (!list.hasSelection) {
|
||||
<span i18n>{list.collectionSize, plural, =1 {One document} other {{{list.collectionSize || 0}} documents}}</span>
|
||||
} @if (isFiltered) {
|
||||
<span i18n>(filtered)</span>
|
||||
@@ -142,7 +142,7 @@
|
||||
<i-bs width="1em" height="1em" name="x"></i-bs><small i18n>Reset filters</small>
|
||||
</button>
|
||||
}
|
||||
@if (!list.isReloading && list.selected.size > 0) {
|
||||
@if (!list.isReloading && list.hasSelection) {
|
||||
<button class="btn btn-link py-0" (click)="list.selectNone()">
|
||||
<i-bs width="1em" height="1em" name="slash-circle" class="me-1"></i-bs><small i18n>Clear selection</small>
|
||||
</button>
|
||||
|
||||
@@ -240,7 +240,7 @@ export class DocumentListComponent
|
||||
}
|
||||
|
||||
get isBulkEditing(): boolean {
|
||||
return this.list.selected.size > 0
|
||||
return this.list.hasSelection
|
||||
}
|
||||
|
||||
toggleDisplayField(field: DisplayField) {
|
||||
@@ -327,7 +327,7 @@ export class DocumentListComponent
|
||||
})
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe(() => {
|
||||
if (this.list.selected.size > 0) {
|
||||
if (this.list.hasSelection) {
|
||||
this.list.selectNone()
|
||||
} else if (this.isFiltered) {
|
||||
this.resetFilters()
|
||||
@@ -356,7 +356,7 @@ export class DocumentListComponent
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe(() => {
|
||||
if (this.list.documents.length > 0) {
|
||||
if (this.list.selected.size > 0) {
|
||||
if (this.list.hasSelection) {
|
||||
this.openDocumentDetail(Array.from(this.list.selected)[0])
|
||||
} else {
|
||||
this.openDocumentDetail(this.list.documents[0])
|
||||
|
||||
@@ -510,12 +510,16 @@ describe('DocumentListViewService', () => {
|
||||
})
|
||||
|
||||
it('should support select all', () => {
|
||||
documentListViewService.selectAll()
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
documentListViewService.reload()
|
||||
const reloadReq = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
req.flush(full_results)
|
||||
expect(reloadReq.request.method).toEqual('GET')
|
||||
reloadReq.flush(full_results)
|
||||
|
||||
documentListViewService.selectAll()
|
||||
expect(documentListViewService.allSelected).toBeTruthy()
|
||||
expect(documentListViewService.selectedCount).toEqual(documents.length)
|
||||
expect(documentListViewService.selected.size).toEqual(documents.length)
|
||||
expect(documentListViewService.isSelected(documents[0])).toBeTruthy()
|
||||
documentListViewService.selectNone()
|
||||
@@ -552,25 +556,25 @@ describe('DocumentListViewService', () => {
|
||||
})
|
||||
|
||||
it('should support selection range reduction', () => {
|
||||
documentListViewService.selectAll()
|
||||
documentListViewService.reload()
|
||||
let req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
req.flush(full_results)
|
||||
|
||||
documentListViewService.selectAll()
|
||||
expect(documentListViewService.selected.size).toEqual(6)
|
||||
|
||||
documentListViewService.setFilterRules(filterRules)
|
||||
httpTestingController.expectOne(
|
||||
req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true&tags__id__all=9`
|
||||
)
|
||||
const reqs = httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id&tags__id__all=9`
|
||||
)
|
||||
reqs[0].flush({
|
||||
req.flush({
|
||||
count: 3,
|
||||
results: documents.slice(0, 3),
|
||||
})
|
||||
expect(documentListViewService.allSelected).toBeTruthy()
|
||||
expect(documentListViewService.selected.size).toEqual(3)
|
||||
})
|
||||
|
||||
|
||||
@@ -66,6 +66,11 @@ export interface ListViewState {
|
||||
*/
|
||||
selected?: Set<number>
|
||||
|
||||
/**
|
||||
* True if the full filtered result set is selected.
|
||||
*/
|
||||
allSelected?: boolean
|
||||
|
||||
/**
|
||||
* The page size of the list view.
|
||||
*/
|
||||
@@ -166,6 +171,20 @@ export class DocumentListViewService {
|
||||
sortReverse: true,
|
||||
filterRules: [],
|
||||
selected: new Set<number>(),
|
||||
allSelected: false,
|
||||
}
|
||||
}
|
||||
|
||||
private syncSelectedToCurrentPage() {
|
||||
if (!this.allSelected) {
|
||||
return
|
||||
}
|
||||
|
||||
this.selected.clear()
|
||||
this.documents?.forEach((doc) => this.selected.add(doc.id))
|
||||
|
||||
if (!this.collectionSize) {
|
||||
this.selectNone()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,6 +291,7 @@ export class DocumentListViewService {
|
||||
activeListViewState.collectionSize = result.count
|
||||
activeListViewState.documents = result.results
|
||||
this.selectionData = resultWithSelectionData.selection_data ?? null
|
||||
this.syncSelectedToCurrentPage()
|
||||
|
||||
if (updateQueryParams && !this._activeSavedViewId) {
|
||||
let base = ['/documents']
|
||||
@@ -404,6 +424,20 @@ export class DocumentListViewService {
|
||||
return this.activeListViewState.selected
|
||||
}
|
||||
|
||||
get allSelected(): boolean {
|
||||
return this.activeListViewState.allSelected ?? false
|
||||
}
|
||||
|
||||
get selectedCount(): number {
|
||||
return this.allSelected
|
||||
? (this.collectionSize ?? this.selected.size)
|
||||
: this.selected.size
|
||||
}
|
||||
|
||||
get hasSelection(): boolean {
|
||||
return this.allSelected || this.selected.size > 0
|
||||
}
|
||||
|
||||
setSort(field: string, reverse: boolean) {
|
||||
this.activeListViewState.sortField = field
|
||||
this.activeListViewState.sortReverse = reverse
|
||||
@@ -558,11 +592,16 @@ export class DocumentListViewService {
|
||||
}
|
||||
|
||||
selectNone() {
|
||||
this.activeListViewState.allSelected = false
|
||||
this.selected.clear()
|
||||
this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null
|
||||
}
|
||||
|
||||
reduceSelectionToFilter() {
|
||||
if (this.allSelected) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.selected.size > 0) {
|
||||
this.documentService
|
||||
.listAllFilteredIds(this.filterRules)
|
||||
@@ -577,12 +616,12 @@ export class DocumentListViewService {
|
||||
}
|
||||
|
||||
selectAll() {
|
||||
this.documentService
|
||||
.listAllFilteredIds(this.filterRules)
|
||||
.subscribe((ids) => ids.forEach((id) => this.selected.add(id)))
|
||||
this.activeListViewState.allSelected = true
|
||||
this.syncSelectedToCurrentPage()
|
||||
}
|
||||
|
||||
selectPage() {
|
||||
this.activeListViewState.allSelected = false
|
||||
this.selected.clear()
|
||||
this.documents.forEach((doc) => {
|
||||
this.selected.add(doc.id)
|
||||
@@ -590,10 +629,13 @@ export class DocumentListViewService {
|
||||
}
|
||||
|
||||
isSelected(d: Document) {
|
||||
return this.selected.has(d.id)
|
||||
return this.allSelected || this.selected.has(d.id)
|
||||
}
|
||||
|
||||
toggleSelected(d: Document): void {
|
||||
if (this.allSelected) {
|
||||
this.activeListViewState.allSelected = false
|
||||
}
|
||||
if (this.selected.has(d.id)) this.selected.delete(d.id)
|
||||
else this.selected.add(d.id)
|
||||
this.rangeSelectionAnchorIndex = this.documentIndexInCurrentView(d.id)
|
||||
@@ -601,6 +643,10 @@ export class DocumentListViewService {
|
||||
}
|
||||
|
||||
selectRangeTo(d: Document) {
|
||||
if (this.allSelected) {
|
||||
this.activeListViewState.allSelected = false
|
||||
}
|
||||
|
||||
if (this.rangeSelectionAnchorIndex !== null) {
|
||||
const documentToIndex = this.documentIndexInCurrentView(d.id)
|
||||
const fromIndex = Math.min(
|
||||
|
||||
Reference in New Issue
Block a user