1
0
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:
Shane Melton 2024-08-22 07:40:32 -07:00 committed by GitHub
parent 7da1082849
commit ade01c9d07
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 79 additions and 47 deletions

View File

@ -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>

View File

@ -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();

View File

@ -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>

View File

@ -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.
*/

View File

@ -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>

View File

@ -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],
})

View File

@ -1 +1,3 @@
export * from "./item.module";
export { BitItemHeight, BitItemHeightClass } from "./item.component";

View File

@ -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,