mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 08:13:42 +00:00
Merge pull request #1858 from bitwarden/cdk-virtual-scroll
Use cdk-virtual-scroll for long cipher lists
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
|
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||||
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/services.module';
|
import { ServicesModule } from './services/services.module';
|
||||||
@@ -68,7 +68,7 @@ import { I18nPipe } from 'jslib-angular/pipes/i18n.pipe';
|
|||||||
import { SearchCiphersPipe } from 'jslib-angular/pipes/search-ciphers.pipe';
|
import { SearchCiphersPipe } from 'jslib-angular/pipes/search-ciphers.pipe';
|
||||||
|
|
||||||
import { ActionButtonsComponent } from './components/action-buttons.component';
|
import { ActionButtonsComponent } from './components/action-buttons.component';
|
||||||
import { CiphersListComponent } from './components/ciphers-list.component';
|
import { CipherRowComponent } from './components/cipher-row.component';
|
||||||
import { PopOutComponent } from './components/pop-out.component';
|
import { PopOutComponent } from './components/pop-out.component';
|
||||||
import { SendListComponent } from './components/send-list.component';
|
import { SendListComponent } from './components/send-list.component';
|
||||||
|
|
||||||
@@ -170,8 +170,8 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
|||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
ServicesModule,
|
ServicesModule,
|
||||||
ToasterModule.forRoot(),
|
ToasterModule.forRoot(),
|
||||||
InfiniteScrollModule,
|
|
||||||
DragDropModule,
|
DragDropModule,
|
||||||
|
ScrollingModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
A11yTitleDirective,
|
A11yTitleDirective,
|
||||||
@@ -184,8 +184,8 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
|||||||
BlurClickDirective,
|
BlurClickDirective,
|
||||||
BoxRowDirective,
|
BoxRowDirective,
|
||||||
CalloutComponent,
|
CalloutComponent,
|
||||||
|
CipherRowComponent,
|
||||||
CiphersComponent,
|
CiphersComponent,
|
||||||
CiphersListComponent,
|
|
||||||
CollectionsComponent,
|
CollectionsComponent,
|
||||||
ColorPasswordPipe,
|
ColorPasswordPipe,
|
||||||
CurrentTabComponent,
|
CurrentTabComponent,
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
<a *ngFor="let c of ciphers" (click)="selectCipher(c)" (dblclick)="launchCipher(c)" href="#" appStopClick
|
<a (click)="selectCipher(cipher)" (dblclick)="launchCipher(cipher)" href="#" appStopClick
|
||||||
title="{{title}} - {{c.name}}" class="box-content-row box-content-row-flex">
|
title="{{title}} - {{cipher.name}}" class="box-content-row box-content-row-flex">
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<app-vault-icon [cipher]="c"></app-vault-icon>
|
<app-vault-icon [cipher]="cipher"></app-vault-icon>
|
||||||
<div class="row-main-content">
|
<div class="row-main-content">
|
||||||
<span class="text">
|
<span class="text">
|
||||||
{{c.name}}
|
{{cipher.name}}
|
||||||
<ng-container *ngIf="c.organizationId">
|
<ng-container *ngIf="cipher.organizationId">
|
||||||
<i class="fa fa-share-alt text-muted" title="{{'shared' | i18n}}" aria-hidden="true"></i>
|
<i class="fa fa-share-alt text-muted" title="{{'shared' | i18n}}" aria-hidden="true"></i>
|
||||||
<span class="sr-only">{{'shared' | i18n}}</span>
|
<span class="sr-only">{{'shared' | i18n}}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="c.hasAttachments">
|
<ng-container *ngIf="cipher.hasAttachments">
|
||||||
<i class="fa fa-paperclip text-muted" title="{{'attachments' | i18n}}" aria-hidden="true"></i>
|
<i class="fa fa-paperclip text-muted" title="{{'attachments' | i18n}}" aria-hidden="true"></i>
|
||||||
<span class="sr-only">{{'attachments' | i18n}}</span>
|
<span class="sr-only">{{'attachments' | i18n}}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</span>
|
</span>
|
||||||
<span class="detail">{{c.subTitle}}</span>
|
<span class="detail">{{cipher.subTitle}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<app-action-buttons [cipher]="c" [showView]="showView" (onView)="viewCipher(c)" (launchEvent)="launchCipher(c)"
|
<app-action-buttons [cipher]="cipher" [showView]="showView" (onView)="viewCipher(cipher)" (launchEvent)="launchCipher(cipher)"
|
||||||
class="action-buttons">
|
class="action-buttons">
|
||||||
</app-action-buttons>
|
</app-action-buttons>
|
||||||
</a>
|
</a>
|
||||||
@@ -5,24 +5,20 @@ import {
|
|||||||
Output,
|
Output,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { CipherType } from 'jslib-common/enums/cipherType';
|
|
||||||
|
|
||||||
import { CipherView } from 'jslib-common/models/view/cipherView';
|
import { CipherView } from 'jslib-common/models/view/cipherView';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-ciphers-list',
|
selector: 'app-cipher-row',
|
||||||
templateUrl: 'ciphers-list.component.html',
|
templateUrl: 'cipher-row.component.html',
|
||||||
})
|
})
|
||||||
export class CiphersListComponent {
|
export class CipherRowComponent {
|
||||||
@Output() onSelected = new EventEmitter<CipherView>();
|
@Output() onSelected = new EventEmitter<CipherView>();
|
||||||
@Output() launchEvent = new EventEmitter<CipherView>();
|
@Output() launchEvent = new EventEmitter<CipherView>();
|
||||||
@Output() onView = new EventEmitter<CipherView>();
|
@Output() onView = new EventEmitter<CipherView>();
|
||||||
@Input() ciphers: CipherView[];
|
@Input() cipher: CipherView;
|
||||||
@Input() showView = false;
|
@Input() showView = false;
|
||||||
@Input() title: string;
|
@Input() title: string;
|
||||||
|
|
||||||
cipherType = CipherType;
|
|
||||||
|
|
||||||
selectCipher(c: CipherView) {
|
selectCipher(c: CipherView) {
|
||||||
this.onSelected.emit(c);
|
this.onSelected.emit(c);
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ main {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
content::-webkit-scrollbar {
|
content::-webkit-scrollbar, cdk-virtual-scroll-viewport::-webkit-scrollbar {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
}
|
}
|
||||||
@@ -109,7 +109,13 @@ content::-webkit-scrollbar-track {
|
|||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
content::-webkit-scrollbar-thumb {
|
cdk-virtual-scroll-viewport::-webkit-scrollbar-track {
|
||||||
|
@include themify($themes) {
|
||||||
|
background-color: themed('backgroundColor');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
content::-webkit-scrollbar-thumb, cdk-virtual-scroll-viewport::-webkit-scrollbar-thumb {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
margin-right: 1px;
|
margin-right: 1px;
|
||||||
|
|
||||||
@@ -394,3 +400,15 @@ content {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cdk-virtual-scroll
|
||||||
|
.cdk-virtual-scroll-viewport {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cdk-virtual-scroll-content-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,14 +25,14 @@ export class PopupUtilsService {
|
|||||||
win.location.search.indexOf('uilocation=popup') > -1;
|
win.location.search.indexOf('uilocation=popup') > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
getContentScrollY(win: Window): number {
|
getContentScrollY(win: Window, scrollingContainer: string = 'content'): number {
|
||||||
const content = win.document.getElementsByTagName('content')[0];
|
const content = win.document.getElementsByTagName(scrollingContainer)[0];
|
||||||
return content.scrollTop;
|
return content.scrollTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
setContentScrollY(win: Window, scrollY: number): void {
|
setContentScrollY(win: Window, scrollY: number, scrollingContainer: string = 'content'): void {
|
||||||
if (scrollY != null) {
|
if (scrollY != null) {
|
||||||
const content = win.document.getElementsByTagName('content')[0];
|
const content = win.document.getElementsByTagName(scrollingContainer)[0];
|
||||||
content.scrollTop = scrollY;
|
content.scrollTop = scrollY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,8 +53,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="(isPaging() ? pagedCiphers : ciphers) as filteredCiphers">
|
<ng-container *ngIf="ciphers">
|
||||||
<div class="no-items" *ngIf="!filteredCiphers.length">
|
<div class="no-items" *ngIf="!ciphers.length">
|
||||||
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded" aria-hidden="true"></i>
|
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded" aria-hidden="true"></i>
|
||||||
<ng-container *ngIf="loaded">
|
<ng-container *ngIf="loaded">
|
||||||
<p>{{'noItemsInList' | i18n}}</p>
|
<p>{{'noItemsInList' | i18n}}</p>
|
||||||
@@ -63,17 +63,17 @@
|
|||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<div class="box list only-list" *ngIf="filteredCiphers.length" infiniteScroll [infiniteScrollDistance]="1"
|
<cdk-virtual-scroll-viewport itemSize="46" *ngIf="ciphers.length" #virtualScrollViewport>
|
||||||
[infiniteScrollContainer]="'content'" [fromRoot]="true" [infiniteScrollDisabled]="!isPaging()"
|
<div class="box list only-list">
|
||||||
(scrolled)="loadMore()">
|
<div class="box-header">
|
||||||
<div class="box-header">
|
{{groupingTitle}}
|
||||||
{{groupingTitle}}
|
<span class="flex-right">{{isSearching() ? ciphers.length : ciphers.length}}</span>
|
||||||
<span class="flex-right">{{isSearching() ? filteredCiphers.length : ciphers.length}}</span>
|
</div>
|
||||||
|
<div class="box-content">
|
||||||
|
<app-cipher-row *cdkVirtualFor="let c of ciphers" [cipher]="c" title="{{'viewItem' | i18n}}"
|
||||||
|
(onSelected)="selectCipher($event)" (launchEvent)="launchCipher($event)"></app-cipher-row>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
</cdk-virtual-scroll-viewport>
|
||||||
<app-ciphers-list [ciphers]="filteredCiphers" title="{{'viewItem' | i18n}}"
|
|
||||||
(onSelected)="selectCipher($event)" (launchEvent)="launchCipher($event)"></app-ciphers-list>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</content>
|
</content>
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On
|
|||||||
private selectedTimeout: number;
|
private selectedTimeout: number;
|
||||||
private preventSelected = false;
|
private preventSelected = false;
|
||||||
private applySavedState = true;
|
private applySavedState = true;
|
||||||
|
private scrollingContainer = 'cdk-virtual-scroll-viewport';
|
||||||
|
|
||||||
constructor(searchService: SearchService, private route: ActivatedRoute,
|
constructor(searchService: SearchService, private route: ActivatedRoute,
|
||||||
private router: Router, private location: Location,
|
private router: Router, private location: Location,
|
||||||
@@ -63,7 +64,6 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On
|
|||||||
private folderService: FolderService, private collectionService: CollectionService,
|
private folderService: FolderService, private collectionService: CollectionService,
|
||||||
private platformUtilsService: PlatformUtilsService, private cipherService: CipherService) {
|
private platformUtilsService: PlatformUtilsService, private cipherService: CipherService) {
|
||||||
super(searchService);
|
super(searchService);
|
||||||
this.pageSize = 100;
|
|
||||||
this.applySavedState = (window as any).previousPopupUrl != null &&
|
this.applySavedState = (window as any).previousPopupUrl != null &&
|
||||||
!(window as any).previousPopupUrl.startsWith('/ciphers');
|
!(window as any).previousPopupUrl.startsWith('/ciphers');
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,8 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.applySavedState && this.state != null) {
|
if (this.applySavedState && this.state != null) {
|
||||||
window.setTimeout(() => this.popupUtils.setContentScrollY(window, this.state.scrollY), 0);
|
window.setTimeout(() => this.popupUtils.setContentScrollY(window, this.state.scrollY,
|
||||||
|
this.scrollingContainer), 0);
|
||||||
}
|
}
|
||||||
this.stateService.remove(ComponentId);
|
this.stateService.remove(ComponentId);
|
||||||
if (queryParamsSub != null) {
|
if (queryParamsSub != null) {
|
||||||
@@ -227,7 +228,7 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On
|
|||||||
|
|
||||||
private async saveState() {
|
private async saveState() {
|
||||||
this.state = {
|
this.state = {
|
||||||
scrollY: this.popupUtils.getContentScrollY(window),
|
scrollY: this.popupUtils.getContentScrollY(window, this.scrollingContainer),
|
||||||
searchText: this.searchText,
|
searchText: this.searchText,
|
||||||
};
|
};
|
||||||
await this.stateService.save(ComponentId, this.state);
|
await this.stateService.save(ComponentId, this.state);
|
||||||
|
|||||||
@@ -27,9 +27,10 @@
|
|||||||
<span class="flex-right">{{loginCiphers.length}}</span>
|
<span class="flex-right">{{loginCiphers.length}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<app-ciphers-list [ciphers]="loginCiphers" title="{{'autoFill' | i18n}}" [showView]="true"
|
<app-cipher-row *ngFor="let loginCipher of loginCiphers" [cipher]="loginCipher"
|
||||||
(onSelected)="fillCipher($event)" (onView)="viewCipher($event)" *ngIf="loginCiphers.length">
|
title="{{'autoFill' | i18n}}" [showView]="true" (onSelected)="fillCipher($event)"
|
||||||
</app-ciphers-list>
|
(onView)="viewCipher($event)">
|
||||||
|
</app-cipher-row>
|
||||||
<div class="box-content-row padded no-hover" *ngIf="!loginCiphers.length">
|
<div class="box-content-row padded no-hover" *ngIf="!loginCiphers.length">
|
||||||
<p class="text-center">{{'autoFillInfo' | i18n}}</p>
|
<p class="text-center">{{'autoFillInfo' | i18n}}</p>
|
||||||
<button type="button" class="btn primary link block" (click)="addCipher()">
|
<button type="button" class="btn primary link block" (click)="addCipher()">
|
||||||
@@ -44,8 +45,8 @@
|
|||||||
<span class="flex-right">{{cardCiphers.length}}</span>
|
<span class="flex-right">{{cardCiphers.length}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<app-ciphers-list [ciphers]="cardCiphers" title="{{'autoFill' | i18n}}" [showView]="true"
|
<app-cipher-row *ngFor="let cardCipher of cardCiphers" [cipher]="cardCipher" title="{{'autoFill' | i18n}}" [showView]="true"
|
||||||
(onSelected)="fillCipher($event)" (onView)="viewCipher($event)"></app-ciphers-list>
|
(onSelected)="fillCipher($event)" (onView)="viewCipher($event)"></app-cipher-row>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box list" *ngIf="identityCiphers && identityCiphers.length">
|
<div class="box list" *ngIf="identityCiphers && identityCiphers.length">
|
||||||
@@ -54,8 +55,8 @@
|
|||||||
<span class="flex-right">{{identityCiphers.length}}</span>
|
<span class="flex-right">{{identityCiphers.length}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<app-ciphers-list [ciphers]="identityCiphers" title="{{'autoFill' | i18n}}" [showView]="true"
|
<app-cipher-row *ngFor="let identityCipher of identityCiphers" [cipher]="identityCipher" title="{{'autoFill' | i18n}}" [showView]="true"
|
||||||
(onSelected)="fillCipher($event)" (onView)="viewCipher($event)"></app-ciphers-list>
|
(onSelected)="fillCipher($event)" (onView)="viewCipher($event)"></app-cipher-row>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|||||||
@@ -29,8 +29,9 @@
|
|||||||
<span class="flex-right">{{favoriteCiphers.length}}</span>
|
<span class="flex-right">{{favoriteCiphers.length}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<app-ciphers-list [ciphers]="favoriteCiphers" title="{{'viewItem' | i18n}}"
|
<app-cipher-row *ngFor="let favoriteCipher of favoriteCiphers" [cipher]="favoriteCipher"
|
||||||
(onSelected)="selectCipher($event)" (launchEvent)="launchCipher($event)"></app-ciphers-list>
|
title="{{'viewItem' | i18n}}" (onSelected)="selectCipher($event)"
|
||||||
|
(launchEvent)="launchCipher($event)"></app-cipher-row>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box list">
|
<div class="box list">
|
||||||
@@ -74,7 +75,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box list" *ngIf="nestedFolders && nestedFolders.length">
|
<div class="box list" *ngIf="nestedFolders?.length">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'folders' | i18n}}
|
{{'folders' | i18n}}
|
||||||
<span class="flex-right">{{folderCount}}</span>
|
<span class="flex-right">{{folderCount}}</span>
|
||||||
@@ -100,13 +101,13 @@
|
|||||||
<span class="flex-right">{{nestedCollections.length}}</span>
|
<span class="flex-right">{{nestedCollections.length}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content single-line">
|
<div class="box-content single-line">
|
||||||
<a *ngFor="let c of nestedCollections" href="#" class="box-content-row" appStopClick appBlurClick
|
<a *ngFor="let nestedCollection of nestedCollections" href="#" class="box-content-row"
|
||||||
(click)="selectCollection(c.node)">
|
appStopClick appBlurClick (click)="selectCollection(nestedCollection.node)">
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<div class="icon"><i class="fa fa-fw fa-lg fa-cube"></i></div>
|
<div class="icon"><i class="fa fa-fw fa-lg fa-cube"></i></div>
|
||||||
<span class="text">{{c.node.name}}</span>
|
<span class="text">{{nestedCollection.node.name}}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="row-sub-label">{{collectionCounts.get(c.node.id) || 0}}</span>
|
<span class="row-sub-label">{{collectionCounts.get(nestedCollection.node.id) || 0}}</span>
|
||||||
<span><i class="fa fa-chevron-right fa-lg row-sub-icon"></i></span>
|
<span><i class="fa fa-chevron-right fa-lg row-sub-icon"></i></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -117,8 +118,9 @@
|
|||||||
<div class="flex-right">{{noFolderCiphers.length}}</div>
|
<div class="flex-right">{{noFolderCiphers.length}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<app-ciphers-list [ciphers]="noFolderCiphers" title="{{'viewItem' | i18n}}"
|
<app-cipher-row *ngFor="let noFolderCipher of noFolderCiphers" [cipher]="noFolderCipher"
|
||||||
(onSelected)="selectCipher($event)" (launchEvent)="launchCipher($event)"></app-ciphers-list>
|
title="{{'viewItem' | i18n}}" (onSelected)="selectCipher($event)"
|
||||||
|
(launchEvent)="launchCipher($event)"></app-cipher-row>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box list" *ngIf="deletedCount">
|
<div class="box list" *ngIf="deletedCount">
|
||||||
@@ -143,11 +145,14 @@
|
|||||||
<div class="no-items" *ngIf="!ciphers || !ciphers.length">
|
<div class="no-items" *ngIf="!ciphers || !ciphers.length">
|
||||||
<p>{{'noItemsInList' | i18n}}</p>
|
<p>{{'noItemsInList' | i18n}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="box list full-list" *ngIf="ciphers && ciphers.length > 0">
|
<cdk-virtual-scroll-viewport itemSize="46" *ngIf="ciphers && ciphers.length > 0">
|
||||||
<div class="box-content">
|
<div class="box list full-list">
|
||||||
<app-ciphers-list [ciphers]="ciphers" title="{{'viewItem' | i18n}}" (onSelected)="selectCipher($event)"
|
<div class="box-content">
|
||||||
(launchEvent)="launchCipher($event)"></app-ciphers-list>
|
<app-cipher-row *cdkVirtualFor="let searchedCipher of ciphers" [cipher]="searchedCipher"
|
||||||
|
title="{{'viewItem' | i18n}}" (onSelected)="selectCipher($event)"
|
||||||
|
(launchEvent)="launchCipher($event)"></app-cipher-row>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</cdk-virtual-scroll-viewport>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</content>
|
</content>
|
||||||
|
|||||||
@@ -19,8 +19,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"preserveWhitespaces": true,
|
"preserveWhitespaces": true
|
||||||
"enableIvy": false
|
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src"
|
"src"
|
||||||
|
|||||||
Reference in New Issue
Block a user