mirror of
https://github.com/bitwarden/browser
synced 2024-12-23 16:41:48 +01:00
[PM-8380] Browser Refresh - Virtual scrolling (#10646)
* [PM-8380] Add option to disable content padding for popup page * [PM-8380] Add cdkVirtualScroll to vault list items and fixed item heights * [PM-8380] Move item height constants to item component
This commit is contained in:
parent
7da1082849
commit
ade01c9d07
@ -17,7 +17,8 @@
|
||||
[ngClass]="{ 'tw-invisible': loading }"
|
||||
>
|
||||
<div
|
||||
class="tw-max-w-screen-sm tw-mx-auto tw-p-3 tw-flex-1 tw-flex tw-flex-col tw-h-full tw-w-full"
|
||||
class="tw-max-w-screen-sm tw-mx-auto tw-flex-1 tw-flex tw-flex-col tw-h-full tw-w-full"
|
||||
[ngClass]="{ 'tw-p-3': !disablePadding }"
|
||||
>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, Input, inject, signal } from "@angular/core";
|
||||
import { booleanAttribute, Component, inject, Input, signal } from "@angular/core";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
|
||||
@ -17,6 +17,9 @@ export class PopupPageComponent {
|
||||
|
||||
@Input() loading = false;
|
||||
|
||||
@Input({ transform: booleanAttribute })
|
||||
disablePadding = false;
|
||||
|
||||
protected scrolled = signal(false);
|
||||
isScrolled = this.scrolled.asReadonly();
|
||||
|
||||
|
@ -17,47 +17,50 @@
|
||||
{{ description }}
|
||||
</div>
|
||||
<bit-item-group>
|
||||
<bit-item *ngFor="let cipher of ciphers">
|
||||
<a
|
||||
bit-item-content
|
||||
(click)="onViewCipher(cipher)"
|
||||
[appA11yTitle]="'viewItemTitle' | i18n: cipher.name"
|
||||
>
|
||||
<app-vault-icon slot="start" [cipher]="cipher"></app-vault-icon>
|
||||
<span data-testid="item-name">{{ cipher.name }}</span>
|
||||
<i
|
||||
*ngIf="cipher.organizationId"
|
||||
appOrgIcon
|
||||
[tierType]="cipher.organization.productTierType"
|
||||
[size]="'small'"
|
||||
[appA11yTitle]="orgIconTooltip(cipher)"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="cipher.hasAttachments"
|
||||
class="bwi bwi-paperclip bwi-sm"
|
||||
[appA11yTitle]="'attachments' | i18n"
|
||||
></i>
|
||||
<span slot="secondary">{{ cipher.subTitle }}</span>
|
||||
</a>
|
||||
<ng-container slot="end">
|
||||
<bit-item-action *ngIf="showAutofillButton">
|
||||
<button
|
||||
type="button"
|
||||
bitBadge
|
||||
variant="primary"
|
||||
(click)="doAutofill(cipher)"
|
||||
[title]="'autofillTitle' | i18n: cipher.name"
|
||||
[attr.aria-label]="'autofillTitle' | i18n: cipher.name"
|
||||
>
|
||||
{{ "autoFill" | i18n }}
|
||||
</button>
|
||||
</bit-item-action>
|
||||
<app-item-copy-actions [cipher]="cipher"></app-item-copy-actions>
|
||||
<app-item-more-options
|
||||
[cipher]="cipher"
|
||||
[hideAutofillOptions]="showAutofillButton"
|
||||
></app-item-more-options>
|
||||
</ng-container>
|
||||
</bit-item>
|
||||
<cdk-virtual-scroll-viewport [itemSize]="ItemHeight">
|
||||
<bit-item *cdkVirtualFor="let cipher of ciphers">
|
||||
<a
|
||||
bit-item-content
|
||||
(click)="onViewCipher(cipher)"
|
||||
[appA11yTitle]="'viewItemTitle' | i18n: cipher.name"
|
||||
class="{{ ItemHeightClass }}"
|
||||
>
|
||||
<app-vault-icon slot="start" [cipher]="cipher"></app-vault-icon>
|
||||
<span data-testid="item-name">{{ cipher.name }}</span>
|
||||
<i
|
||||
*ngIf="cipher.organizationId"
|
||||
appOrgIcon
|
||||
[tierType]="cipher.organization.productTierType"
|
||||
[size]="'small'"
|
||||
[appA11yTitle]="orgIconTooltip(cipher)"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="cipher.hasAttachments"
|
||||
class="bwi bwi-paperclip bwi-sm"
|
||||
[appA11yTitle]="'attachments' | i18n"
|
||||
></i>
|
||||
<span slot="secondary">{{ cipher.subTitle }}</span>
|
||||
</a>
|
||||
<ng-container slot="end">
|
||||
<bit-item-action *ngIf="showAutofillButton">
|
||||
<button
|
||||
type="button"
|
||||
bitBadge
|
||||
variant="primary"
|
||||
(click)="doAutofill(cipher)"
|
||||
[title]="'autofillTitle' | i18n: cipher.name"
|
||||
[attr.aria-label]="'autofillTitle' | i18n: cipher.name"
|
||||
>
|
||||
{{ "autoFill" | i18n }}
|
||||
</button>
|
||||
</bit-item-action>
|
||||
<app-item-copy-actions [cipher]="cipher"></app-item-copy-actions>
|
||||
<app-item-more-options
|
||||
[cipher]="cipher"
|
||||
[hideAutofillOptions]="showAutofillButton"
|
||||
></app-item-more-options>
|
||||
</ng-container>
|
||||
</bit-item>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
</bit-item-group>
|
||||
</bit-section>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { ScrollingModule } from "@angular/cdk/scrolling";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { booleanAttribute, Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
import { Router, RouterLink } from "@angular/router";
|
||||
@ -6,6 +7,8 @@ import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import {
|
||||
BadgeModule,
|
||||
BitItemHeight,
|
||||
BitItemHeightClass,
|
||||
ButtonModule,
|
||||
IconButtonModule,
|
||||
ItemModule,
|
||||
@ -35,12 +38,16 @@ import { ItemMoreOptionsComponent } from "../item-more-options/item-more-options
|
||||
ItemCopyActionsComponent,
|
||||
ItemMoreOptionsComponent,
|
||||
OrgIconDirective,
|
||||
ScrollingModule,
|
||||
],
|
||||
selector: "app-vault-list-items-container",
|
||||
templateUrl: "vault-list-items-container.component.html",
|
||||
standalone: true,
|
||||
})
|
||||
export class VaultListItemsContainerComponent {
|
||||
protected ItemHeightClass = BitItemHeightClass;
|
||||
protected ItemHeight = BitItemHeight;
|
||||
|
||||
/**
|
||||
* The list of ciphers to display.
|
||||
*/
|
||||
|
@ -1,4 +1,4 @@
|
||||
<popup-page [loading]="loading$ | async">
|
||||
<popup-page [loading]="loading$ | async" disablePadding>
|
||||
<popup-header slot="header" [pageTitle]="'vault' | i18n">
|
||||
<ng-container slot="end">
|
||||
<app-new-item-dropdown [initialValues]="newItemItemValues$ | async"></app-new-item-dropdown>
|
||||
@ -54,7 +54,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="vaultState === null">
|
||||
<div *ngIf="vaultState === null" cdkVirtualScrollingElement class="tw-h-full tw-p-3">
|
||||
<app-autofill-vault-list-items></app-autofill-vault-list-items>
|
||||
<app-vault-list-items-container
|
||||
[title]="'favorites' | i18n"
|
||||
@ -67,6 +67,6 @@
|
||||
id="allItems"
|
||||
disableSectionMargin
|
||||
></app-vault-list-items-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
</popup-page>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { ScrollingModule } from "@angular/cdk/scrolling";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
@ -51,6 +52,7 @@ enum VaultState {
|
||||
RouterLink,
|
||||
VaultV2SearchComponent,
|
||||
NewItemDropdownV2Component,
|
||||
ScrollingModule,
|
||||
],
|
||||
providers: [VaultUiOnboardingService],
|
||||
})
|
||||
|
@ -1 +1,3 @@
|
||||
export * from "./item.module";
|
||||
|
||||
export { BitItemHeight, BitItemHeightClass } from "./item.component";
|
||||
|
@ -5,6 +5,20 @@ import { A11yRowDirective } from "../a11y/a11y-row.directive";
|
||||
|
||||
import { ItemActionComponent } from "./item-action.component";
|
||||
|
||||
/**
|
||||
* The class used to set the height of a bit item's inner content.
|
||||
*/
|
||||
export const BitItemHeightClass = `tw-h-[52px]`;
|
||||
|
||||
/**
|
||||
* The height of a bit item in pixels. Includes any margin, padding, or border. Used by the virtual scroll
|
||||
* to estimate how many items can be displayed at once and how large the virtual container should be.
|
||||
* Needs to be updated if the item height or spacing changes.
|
||||
*
|
||||
* 52px + 5.25px bottom margin + 1px border = 58.25px
|
||||
*/
|
||||
export const BitItemHeight = 58.25; //
|
||||
|
||||
@Component({
|
||||
selector: "bit-item",
|
||||
standalone: true,
|
||||
|
Loading…
Reference in New Issue
Block a user