mirror of
https://github.com/bitwarden/desktop.git
synced 2025-01-28 22:11:34 +01:00
Use cdk-virtual-scroll for long cipher lists (#1001)
* Use cdk-virtual-scroll for cipher lists * add trackBy, reorder dom * Undo merge conflict error * Fix layout, increase scrolling buffer * fix linting * Remove unused infinite-scroll directives for Send * Add back refresh method * Update jslib * Fix itemSize and min/maxBufferPx directives * Move refresh() into base class * Use cipherListVirtualScroll strategy * fix linting * Update to use latest virtual-scroll strategy * Update jslib
This commit is contained in:
parent
157d9478d4
commit
816249a48a
2
jslib
2
jslib
@ -1 +1 @@
|
|||||||
Subproject commit 23309d33e2a335574ed898d6543040372d41526a
|
Subproject commit c70c8ecc247cb92e1f867630031fd5cdf124bcd3
|
@ -1,12 +1,12 @@
|
|||||||
import 'zone.js/dist/zone';
|
import 'zone.js/dist/zone';
|
||||||
|
|
||||||
import { ToasterModule } from 'angular2-toaster';
|
import { ToasterModule } from 'angular2-toaster';
|
||||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
|
||||||
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { ServicesModule } from './services.module';
|
import { ServicesModule } from './services.module';
|
||||||
|
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
|
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||||
import { DatePipe } from '@angular/common';
|
import { DatePipe } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
@ -36,6 +36,7 @@ import { ApiActionDirective } from 'jslib-angular/directives/api-action.directiv
|
|||||||
import { AutofocusDirective } from 'jslib-angular/directives/autofocus.directive';
|
import { AutofocusDirective } from 'jslib-angular/directives/autofocus.directive';
|
||||||
import { BlurClickDirective } from 'jslib-angular/directives/blur-click.directive';
|
import { BlurClickDirective } from 'jslib-angular/directives/blur-click.directive';
|
||||||
import { BoxRowDirective } from 'jslib-angular/directives/box-row.directive';
|
import { BoxRowDirective } from 'jslib-angular/directives/box-row.directive';
|
||||||
|
import { CipherListVirtualScroll } from 'jslib-angular/directives/cipherListVirtualScroll.directive';
|
||||||
import { FallbackSrcDirective } from 'jslib-angular/directives/fallback-src.directive';
|
import { FallbackSrcDirective } from 'jslib-angular/directives/fallback-src.directive';
|
||||||
import { SelectCopyDirective } from 'jslib-angular/directives/select-copy.directive';
|
import { SelectCopyDirective } from 'jslib-angular/directives/select-copy.directive';
|
||||||
import { StopClickDirective } from 'jslib-angular/directives/stop-click.directive';
|
import { StopClickDirective } from 'jslib-angular/directives/stop-click.directive';
|
||||||
@ -164,10 +165,10 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
|||||||
BrowserModule,
|
BrowserModule,
|
||||||
DragDropModule,
|
DragDropModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
InfiniteScrollModule,
|
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
ServicesModule,
|
ServicesModule,
|
||||||
ToasterModule.forRoot(),
|
ToasterModule.forRoot(),
|
||||||
|
ScrollingModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
A11yTitleDirective,
|
A11yTitleDirective,
|
||||||
@ -179,6 +180,7 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
|||||||
BlurClickDirective,
|
BlurClickDirective,
|
||||||
BoxRowDirective,
|
BoxRowDirective,
|
||||||
CalloutComponent,
|
CalloutComponent,
|
||||||
|
CipherListVirtualScroll,
|
||||||
CiphersComponent,
|
CiphersComponent,
|
||||||
CollectionsComponent,
|
CollectionsComponent,
|
||||||
ColorPasswordPipe,
|
ColorPasswordPipe,
|
||||||
|
@ -39,8 +39,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="list" *ngIf="filteredSends.length" infiniteScroll [infiniteScrollDistance]="1"
|
<div class="list" *ngIf="filteredSends.length">
|
||||||
[infiniteScrollContainer]="'#items .content'" [fromRoot]="true" (scrolled)="loadMore()">
|
|
||||||
<a *ngFor="let s of filteredSends" appStopClick (click)="selectSend(s.id)"
|
<a *ngFor="let s of filteredSends" appStopClick (click)="selectSend(s.id)"
|
||||||
title="{{'viewItem' | i18n}}" (contextmenu)="viewSendMenu(s)"
|
title="{{'viewItem' | i18n}}" (contextmenu)="viewSendMenu(s)"
|
||||||
[ngClass]="{'active': s.id === sendId}" class="flex-list-item">
|
[ngClass]="{'active': s.id === sendId}" class="flex-list-item">
|
||||||
|
@ -6,13 +6,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<ng-container *ngIf="(isPaging() ? pagedCiphers : ciphers) as filteredCiphers">
|
<cdk-virtual-scroll-viewport itemSize="42" minBufferPx="400" maxBufferPx="600" *ngIf="ciphers.length">
|
||||||
<div class="list" *ngIf="filteredCiphers.length" infiniteScroll [infiniteScrollDistance]="1"
|
<div class="list">
|
||||||
[infiniteScrollContainer]="'#items .content'" [fromRoot]="true" [infiniteScrollDisabled]="!isPaging()"
|
<a *cdkVirtualFor="let c of ciphers; trackBy: trackByFn" appStopClick (click)="selectCipher(c)"
|
||||||
(scrolled)="loadMore()">
|
|
||||||
<a *ngFor="let c of filteredCiphers" appStopClick (click)="selectCipher(c)"
|
|
||||||
(contextmenu)="rightClickCipher(c)" href="#" title="{{'viewItem' | i18n}}"
|
(contextmenu)="rightClickCipher(c)" href="#" title="{{'viewItem' | i18n}}"
|
||||||
[ngClass]="{'active': c.id === activeCipherId}" class="flex-list-item">
|
[ngClass]="{'active': c.id === activeCipherId}" class="flex-list-item virtual-scroll-item">
|
||||||
<app-vault-icon [cipher]="c"></app-vault-icon>
|
<app-vault-icon [cipher]="c"></app-vault-icon>
|
||||||
<div class="flex-cipher-list-item">
|
<div class="flex-cipher-list-item">
|
||||||
<span class="text">
|
<span class="text">
|
||||||
@ -30,15 +28,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="no-items" *ngIf="!filteredCiphers.length">
|
</cdk-virtual-scroll-viewport>
|
||||||
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded" aria-hidden="true"></i>
|
<div class="no-items" *ngIf="!ciphers.length">
|
||||||
<ng-container *ngIf="loaded">
|
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded" aria-hidden="true"></i>
|
||||||
<i class="fa fa-frown-o fa-4x" aria-hidden="true"></i>
|
<ng-container *ngIf="loaded">
|
||||||
<p>{{'noItemsInList' | i18n}}</p>
|
<i class="fa fa-frown-o fa-4x" aria-hidden="true"></i>
|
||||||
<button (click)="addCipher()" class="btn block primary link">{{'addItem' | i18n}}</button>
|
<p>{{'noItemsInList' | i18n}}</p>
|
||||||
</ng-container>
|
<button (click)="addCipher()" class="btn block primary link">{{'addItem' | i18n}}</button>
|
||||||
</div>
|
</ng-container>
|
||||||
</ng-container>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<button appBlurClick (click)="addCipher()" (contextmenu)="addCipherOptions()"
|
<button appBlurClick (click)="addCipher()" (contextmenu)="addCipherOptions()"
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { SearchService } from 'jslib-common/abstractions/search.service';
|
|
||||||
|
|
||||||
import { CiphersComponent as BaseCiphersComponent } from 'jslib-angular/components/ciphers.component';
|
import { CiphersComponent as BaseCiphersComponent } from 'jslib-angular/components/ciphers.component';
|
||||||
|
|
||||||
|
import { CipherView } from 'jslib-common/models/view/cipherView';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vault-ciphers',
|
selector: 'app-vault-ciphers',
|
||||||
templateUrl: 'ciphers.component.html',
|
templateUrl: 'ciphers.component.html',
|
||||||
})
|
})
|
||||||
export class CiphersComponent extends BaseCiphersComponent {
|
export class CiphersComponent extends BaseCiphersComponent {
|
||||||
constructor(searchService: SearchService) {
|
trackByFn(index: number, c: CipherView) {
|
||||||
super(searchService);
|
return c.id;
|
||||||
this.pageSize = 250;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,16 +79,16 @@ textarea {
|
|||||||
resize: vertical;
|
resize: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
div:not(.modal)::-webkit-scrollbar {
|
div:not(.modal)::-webkit-scrollbar, .cdk-virtual-scroll-viewport::-webkit-scrollbar {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div:not(.modal)::-webkit-scrollbar-track {
|
div:not(.modal)::-webkit-scrollbar-track, .cdk-virtual-scroll-viewport::-webkit-scrollbar-track {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
div:not(.modal)::-webkit-scrollbar-thumb {
|
div:not(.modal)::-webkit-scrollbar-thumb, .cdk-virtual-scroll-viewport::-webkit-scrollbar-thumb {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
margin-right: 1px;
|
margin-right: 1px;
|
||||||
|
|
||||||
@ -102,3 +102,15 @@ div:not(.modal)::-webkit-scrollbar-thumb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cdk-virtual-scroll
|
||||||
|
.cdk-virtual-scroll-viewport {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cdk-virtual-scroll-content-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user