feat: improve sorting by count
This commit is contained in:
parent
ac666df4ce
commit
c114425844
@ -22,7 +22,7 @@ test('basic filtering', async ({ page }) => {
|
||||
await page.getByRole('button', { name: 'Correspondent' }).click()
|
||||
await page.getByRole('menuitem', { name: 'Test Correspondent 1' }).click()
|
||||
await page.getByRole('menuitem', { name: 'Correspondent 9' }).click()
|
||||
await expect(page).toHaveURL(/correspondent__id__in=12,1/)
|
||||
await expect(page).toHaveURL(/correspondent__id__in=1,12/)
|
||||
await expect(page.locator('pngx-document-list')).toHaveText(/7 documents/)
|
||||
await page
|
||||
.locator('pngx-filter-editor')
|
||||
|
@ -4113,7 +4113,7 @@
|
||||
"time": 0.554,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://localhost:8000/api/documents/?page=1&page_size=50&ordering=-created&truncate_content=true&correspondent__id__in=12,1",
|
||||
"url": "http://localhost:8000/api/documents/?page=1&page_size=50&ordering=-created&truncate_content=true&correspondent__id__in=1,12",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
@ -4148,7 +4148,7 @@
|
||||
},
|
||||
{
|
||||
"name": "correspondent__id__in",
|
||||
"value": "12,1"
|
||||
"value": "1,12"
|
||||
}
|
||||
],
|
||||
"headersSize": -1,
|
||||
|
@ -35,7 +35,7 @@
|
||||
</div>
|
||||
@if (selectionModel.items) {
|
||||
<div class="items" #buttonItems>
|
||||
@for (item of selectionModel.itemsSorted | filter: filterText; track item; let i = $index) {
|
||||
@for (item of selectionModel.items | filter: filterText; track item; let i = $index) {
|
||||
@if (allowSelectNone || item.id) {
|
||||
<pngx-toggleable-dropdown-button
|
||||
[item]="item" [hideCount]="hideCount(item)" [state]="selectionModel.get(item.id)" [count]="getUpdatedDocumentCount(item.id)" (toggle)="selectionModel.toggle(item.id)" (exclude)="excludeClicked(item.id)" (click)="setButtonItemIndex(i - 1)" [disabled]="disabled">
|
||||
@ -45,13 +45,13 @@
|
||||
</div>
|
||||
}
|
||||
@if (editing) {
|
||||
@if ((selectionModel.itemsSorted | filter: filterText).length === 0 && createRef !== undefined) {
|
||||
@if ((selectionModel.items | filter: filterText).length === 0 && createRef !== undefined) {
|
||||
<button class="list-group-item list-group-item-action bg-light" (click)="createClicked()" [disabled]="disabled">
|
||||
<small class="ms-2"><ng-container i18n>Create</ng-container> "{{filterText}}"</small>
|
||||
<i-bs width="1.5em" height="1em" name="plus"></i-bs>
|
||||
</button>
|
||||
}
|
||||
@if ((selectionModel.itemsSorted | filter: filterText).length > 0) {
|
||||
@if ((selectionModel.items | filter: filterText).length > 0) {
|
||||
<button class="list-group-item list-group-item-action bg-light" (click)="applyClicked()" [disabled]="!modelIsDirty || disabled">
|
||||
<small class="ms-2" [ngClass]="{'fw-bold': modelIsDirty}" i18n>Apply</small>
|
||||
<i-bs width="1.5em" height="1em" name="arrow-right"></i-bs>
|
||||
|
@ -501,11 +501,11 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
component.selectionModel = selectionModel
|
||||
selectionModel.toggle(items[1].id)
|
||||
selectionModel.apply()
|
||||
expect(selectionModel.itemsSorted).toEqual([
|
||||
expect(component.items).toEqual([
|
||||
nullItem,
|
||||
{ id: null, name: 'Null B' },
|
||||
items[1],
|
||||
items[0],
|
||||
items[1],
|
||||
])
|
||||
})
|
||||
|
||||
|
@ -44,32 +44,9 @@ export class FilterableDropdownSelectionModel {
|
||||
|
||||
items: MatchingModel[] = []
|
||||
|
||||
get itemsSorted(): MatchingModel[] {
|
||||
// TODO: this is getting called very often
|
||||
return this.items.sort((a, b) => {
|
||||
if (a.id == null && b.id != null) {
|
||||
return -1
|
||||
} else if (a.id != null && b.id == null) {
|
||||
return 1
|
||||
} else if (
|
||||
this.getNonTemporary(a.id) == ToggleableItemState.NotSelected &&
|
||||
this.getNonTemporary(b.id) != ToggleableItemState.NotSelected
|
||||
) {
|
||||
return 1
|
||||
} else if (
|
||||
this.getNonTemporary(a.id) != ToggleableItemState.NotSelected &&
|
||||
this.getNonTemporary(b.id) == ToggleableItemState.NotSelected
|
||||
) {
|
||||
return -1
|
||||
} else {
|
||||
return a.name.localeCompare(b.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private selectionStates = new Map<number, ToggleableItemState>()
|
||||
|
||||
private temporarySelectionStates = new Map<number, ToggleableItemState>()
|
||||
temporarySelectionStates = new Map<number, ToggleableItemState>()
|
||||
|
||||
getSelectedItems() {
|
||||
return this.items.filter(
|
||||
@ -188,7 +165,7 @@ export class FilterableDropdownSelectionModel {
|
||||
}
|
||||
}
|
||||
|
||||
private getNonTemporary(id: number) {
|
||||
getNonTemporary(id: number) {
|
||||
return this.selectionStates.get(id) || ToggleableItemState.NotSelected
|
||||
}
|
||||
|
||||
@ -335,7 +312,7 @@ export class FilterableDropdownComponent implements OnDestroy, OnInit {
|
||||
@Input()
|
||||
set items(items: MatchingModel[]) {
|
||||
if (items) {
|
||||
this._selectionModel.items = Array.from(items)
|
||||
this._selectionModel.items = this.itemsSorted(items)
|
||||
this._selectionModel.items.unshift({
|
||||
name: $localize`:Filter drop down element to filter for documents with no correspondent/type/tag assigned:Not assigned`,
|
||||
id: null,
|
||||
@ -343,6 +320,65 @@ export class FilterableDropdownComponent implements OnDestroy, OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
itemsSorted(
|
||||
items: MatchingModel[],
|
||||
isEditModeActive: boolean = true
|
||||
): MatchingModel[] {
|
||||
const isUnassignedElement = (a) => a.id == null
|
||||
const getSelectionCount = (a) =>
|
||||
this._documentCounts?.find((c) => c.id === a.id)?.document_count || 0
|
||||
enum priority {
|
||||
NotAssignable = 5,
|
||||
HasTemporarySelection = 4,
|
||||
HasSelection = 3,
|
||||
HasDocuments = 2,
|
||||
Other = 1,
|
||||
}
|
||||
const getPriority = (a): priority => {
|
||||
if (isUnassignedElement(a)) {
|
||||
return priority.NotAssignable
|
||||
} else if (this.selectionModel.temporarySelectionStates.get(a.id)) {
|
||||
return priority.HasTemporarySelection
|
||||
} else if (
|
||||
this.selectionModel.getNonTemporary(a.id) !=
|
||||
ToggleableItemState.NotSelected
|
||||
) {
|
||||
return priority.HasSelection
|
||||
} else if (getSelectionCount(a) > 0) {
|
||||
return priority.HasDocuments
|
||||
} else {
|
||||
return priority.Other
|
||||
}
|
||||
}
|
||||
|
||||
return items
|
||||
.sort((a, b) => {
|
||||
const aPriority = getPriority(a)
|
||||
const bPriority = getPriority(b)
|
||||
|
||||
if (aPriority != bPriority) {
|
||||
return aPriority > bPriority ? -1 : 1
|
||||
} else {
|
||||
if (aPriority >= priority.HasSelection) {
|
||||
return getSelectionCount(a) > getSelectionCount(b) ? -1 : 1
|
||||
} else if (priority.HasDocuments == aPriority) {
|
||||
return a.document_count > b.document_count ? -1 : 1
|
||||
} else {
|
||||
return a.name.localeCompare(b.name)
|
||||
}
|
||||
}
|
||||
})
|
||||
.filter(
|
||||
(a: SelectionDataItem) =>
|
||||
isEditModeActive ||
|
||||
getSelectionCount(a) ||
|
||||
isUnassignedElement(a) ||
|
||||
this.selectionModel.getNonTemporary(a.id) !=
|
||||
ToggleableItemState.NotSelected ||
|
||||
this.selectionModel.temporarySelectionStates.get(a.id)
|
||||
)
|
||||
}
|
||||
|
||||
get items(): MatchingModel[] {
|
||||
return this._selectionModel.items
|
||||
}
|
||||
@ -354,7 +390,7 @@ export class FilterableDropdownComponent implements OnDestroy, OnInit {
|
||||
set selectionModel(model: FilterableDropdownSelectionModel) {
|
||||
if (this.selectionModel) {
|
||||
this.selectionModel.changed.complete()
|
||||
model.items = this.selectionModel.items
|
||||
model.items = this.itemsSorted(this.selectionModel.items)
|
||||
model.manyToOne = this.selectionModel.manyToOne
|
||||
model.singleSelect = this.editing && !this.selectionModel.manyToOne
|
||||
}
|
||||
@ -419,8 +455,14 @@ export class FilterableDropdownComponent implements OnDestroy, OnInit {
|
||||
: !this.selectionModel.isNoneSelected()
|
||||
}
|
||||
|
||||
_documentCounts: SelectionDataItem[] = []
|
||||
@Input()
|
||||
documentCounts: SelectionDataItem[]
|
||||
set documentCounts(documentCounts: SelectionDataItem[]) {
|
||||
this._documentCounts = documentCounts
|
||||
if (documentCounts) {
|
||||
this.selectionModel.items = this.itemsSorted(this.selectionModel.items)
|
||||
}
|
||||
}
|
||||
|
||||
@Input()
|
||||
shortcutKey: string
|
||||
@ -533,8 +575,8 @@ export class FilterableDropdownComponent implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
getUpdatedDocumentCount(id: number) {
|
||||
if (this.documentCounts) {
|
||||
return this.documentCounts.find((c) => c.id === id)?.document_count
|
||||
if (this._documentCounts) {
|
||||
return this._documentCounts.find((c) => c.id === id)?.document_count
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user