- @if (list.selected.size > 0) {
+ @if (list.hasSelection) {
@@ -127,11 +127,11 @@
Loading...
}
- @if (list.selected.size > 0) {
-
{list.collectionSize, plural, =1 {Selected {{list.selected.size}} of one document} other {Selected {{list.selected.size}} of {{list.collectionSize || 0}} documents}}
+ @if (list.hasSelection) {
+
{list.collectionSize, plural, =1 {Selected {{list.selectedCount}} of one document} other {Selected {{list.selectedCount}} of {{list.collectionSize || 0}} documents}}
}
@if (!list.isReloading) {
- @if (list.selected.size === 0) {
+ @if (!list.hasSelection) {
{list.collectionSize, plural, =1 {One document} other {{{list.collectionSize || 0}} documents}}
} @if (isFiltered) {
(filtered)
@@ -142,7 +142,7 @@
Reset filters
}
- @if (!list.isReloading && list.selected.size > 0) {
+ @if (!list.isReloading && list.hasSelection) {
diff --git a/src-ui/src/app/components/document-list/document-list.component.ts b/src-ui/src/app/components/document-list/document-list.component.ts
index 2cd2ccaf3..eb453d4dc 100644
--- a/src-ui/src/app/components/document-list/document-list.component.ts
+++ b/src-ui/src/app/components/document-list/document-list.component.ts
@@ -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])
diff --git a/src-ui/src/app/services/document-list-view.service.spec.ts b/src-ui/src/app/services/document-list-view.service.spec.ts
index de574d6d3..bfbd26ff0 100644
--- a/src-ui/src/app/services/document-list-view.service.spec.ts
+++ b/src-ui/src/app/services/document-list-view.service.spec.ts
@@ -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)
})
diff --git a/src-ui/src/app/services/document-list-view.service.ts b/src-ui/src/app/services/document-list-view.service.ts
index 9413d3562..9983bc62d 100644
--- a/src-ui/src/app/services/document-list-view.service.ts
+++ b/src-ui/src/app/services/document-list-view.service.ts
@@ -66,6 +66,11 @@ export interface ListViewState {
*/
selected?: Set
+ /**
+ * 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(),
+ 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(