diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts index 3511257d6da..96c23d6dd19 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts @@ -260,9 +260,9 @@ export class VaultItemsComponent { } get isAllSelected() { - return this.editableItems - .slice(0, MaxSelectionCount) - .every((item) => this.selection.isSelected(item)); + // Check selection against sorted items to match toggleAll() behavior + const sortedItems = this.getSortedEditableItems(); + return sortedItems.slice(0, MaxSelectionCount).every((item) => this.selection.isSelected(item)); } get isEmpty() { @@ -376,9 +376,30 @@ export class VaultItemsComponent { } protected toggleAll() { - this.isAllSelected - ? this.selection.clear() - : this.selection.select(...this.editableItems.slice(0, MaxSelectionCount)); + if (this.isAllSelected) { + this.selection.clear(); + } else { + const sortedItems = this.getSortedEditableItems(); + this.selection.select(...sortedItems.slice(0, MaxSelectionCount)); + } + } + + /** + * Returns editableItems sorted according to the current table sort configuration. + * This ensures bulk selection matches the visual order displayed to the user. + */ + private getSortedEditableItems(): VaultItem[] { + const currentSort = this.dataSource.sort; + const items = [...this.editableItems]; + + // If no sort function is set, return items in their original order (as displayed in table) + if (!currentSort || !currentSort.fn) { + return items; + } + + // Apply sort function with direction modifier (matches TableDataSource.sortData behavior) + const directionModifier = currentSort.direction === "asc" ? 1 : -1; + return items.sort((a, b) => currentSort.fn(a, b, currentSort.direction) * directionModifier); } protected event(event: VaultItemEvent) { @@ -584,12 +605,13 @@ export class VaultItemsComponent { * Sorts VaultItems, grouping collections before ciphers, and sorting each group alphabetically by name. */ protected sortByName = (a: VaultItem, b: VaultItem, direction: SortDirection) => { - // Collections before ciphers - const collectionCompare = this.prioritizeCollections(a, b, direction); + // Collections before ciphers (direction-independent) + const collectionCompare = this.prioritizeCollections(a, b); if (collectionCompare !== 0) { return collectionCompare; } + // Name comparison (direction-dependent, handled by directionModifier) return this.compareNames(a, b); }; @@ -611,8 +633,8 @@ export class VaultItemsComponent { return null; }; - // Collections before ciphers - const collectionCompare = this.prioritizeCollections(a, b, direction); + // Collections before ciphers (direction-independent) + const collectionCompare = this.prioritizeCollections(a, b); if (collectionCompare !== 0) { return collectionCompare; } @@ -655,8 +677,8 @@ export class VaultItemsComponent { return priorityMap[permission] ?? -1; }; - // Collections before ciphers - const collectionCompare = this.prioritizeCollections(a, b, direction); + // Collections before ciphers (direction-independent) + const collectionCompare = this.prioritizeCollections(a, b); if (collectionCompare !== 0) { return collectionCompare; } @@ -664,11 +686,12 @@ export class VaultItemsComponent { const priorityA = getPermissionPriority(a); const priorityB = getPermissionPriority(b); - // Higher priority first + // Higher priority first (direction-dependent, handled by directionModifier) if (priorityA !== priorityB) { return priorityA - priorityB; } + // Fallback to name comparison (direction-dependent, handled by directionModifier) return this.compareNames(a, b); }; @@ -679,22 +702,19 @@ export class VaultItemsComponent { /** * Sorts VaultItems by prioritizing collections over ciphers. - * Collections are always placed before ciphers, regardless of the sorting direction. + * Always returns -1 for collections before ciphers, regardless of sort direction. + * This comparison is direction-independent; the direction is applied separately via directionModifier. */ - private prioritizeCollections( - a: VaultItem, - b: VaultItem, - direction: SortDirection, - ): number { + private prioritizeCollections(a: VaultItem, b: VaultItem): number { if (a.collection && !b.collection) { - return direction === "asc" ? -1 : 1; + return -1; // a (collection) comes before b (cipher) } if (!a.collection && b.collection) { - return direction === "asc" ? 1 : -1; + return 1; // b (collection) comes before a (cipher) } - return 0; + return 0; // Both are collections or both are ciphers } private hasPersonalItems(): boolean {