1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-19 09:43:23 +00:00

[PM-25037] add optional size input to app-vault-icon to prevent zoom issues (#17640)

This commit is contained in:
Alex
2025-12-09 12:16:21 -05:00
committed by GitHub
parent ee582b2ebe
commit 5dbcb18b6a
3 changed files with 34 additions and 9 deletions

View File

@@ -45,7 +45,11 @@
tabindex="0" tabindex="0"
[attr.aria-label]="'viewItem' | i18n" [attr.aria-label]="'viewItem' | i18n"
> >
<app-vault-icon *ngIf="row.iconCipher" [cipher]="row.iconCipher"></app-vault-icon> <app-vault-icon
*ngIf="row.iconCipher"
[cipher]="row.iconCipher"
[size]="24"
></app-vault-icon>
</td> </td>
<td <td
class="tw-cursor-pointer" class="tw-cursor-pointer"

View File

@@ -1,9 +1,5 @@
<!-- Applying width and height styles directly to synchronize icon sizing between web/browser/desktop --> <!-- Applying width and height styles directly to synchronize icon sizing between web/browser/desktop -->
<div <div class="tw-flex tw-justify-center tw-items-center" [ngStyle]="iconStyle()" aria-hidden="true">
class="tw-flex tw-justify-center tw-items-center"
[ngStyle]="coloredIcon() ? { width: '36px', height: '36px' } : {}"
aria-hidden="true"
>
<ng-container *ngIf="data$ | async as data"> <ng-container *ngIf="data$ | async as data">
@if (data.imageEnabled && data.image) { @if (data.imageEnabled && data.image) {
<img <img
@@ -16,7 +12,7 @@
'tw-invisible tw-absolute': !imageLoaded(), 'tw-invisible tw-absolute': !imageLoaded(),
'tw-size-6': !coloredIcon(), 'tw-size-6': !coloredIcon(),
}" }"
[ngStyle]="coloredIcon() ? { width: '36px', height: '36px' } : {}" [ngStyle]="iconStyle()"
(load)="imageLoaded.set(true)" (load)="imageLoaded.set(true)"
(error)="imageLoaded.set(false)" (error)="imageLoaded.set(false)"
/> />
@@ -28,7 +24,7 @@
'tw-bg-illustration-bg-primary tw-rounded-full': 'tw-bg-illustration-bg-primary tw-rounded-full':
data.icon?.startsWith('bwi-') && coloredIcon(), data.icon?.startsWith('bwi-') && coloredIcon(),
}" }"
[ngStyle]="coloredIcon() ? { width: '36px', height: '36px' } : {}" [ngStyle]="iconStyle()"
> >
<i <i
class="tw-text-muted bwi bwi-lg {{ data.icon }}" class="tw-text-muted bwi bwi-lg {{ data.icon }}"
@@ -36,6 +32,7 @@
color: coloredIcon() ? 'rgb(var(--color-illustration-outline))' : null, color: coloredIcon() ? 'rgb(var(--color-illustration-outline))' : null,
width: data.icon?.startsWith('credit-card') && coloredIcon() ? '36px' : null, width: data.icon?.startsWith('credit-card') && coloredIcon() ? '36px' : null,
height: data.icon?.startsWith('credit-card') && coloredIcon() ? '30px' : null, height: data.icon?.startsWith('credit-card') && coloredIcon() ? '30px' : null,
fontSize: size() ? size() + 'px' : null,
}" }"
></i> ></i>
</div> </div>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, input, signal } from "@angular/core"; import { ChangeDetectionStrategy, Component, computed, input, signal } from "@angular/core";
import { toObservable } from "@angular/core/rxjs-interop"; import { toObservable } from "@angular/core/rxjs-interop";
import { import {
combineLatest, combineLatest,
@@ -32,8 +32,32 @@ export class IconComponent {
*/ */
readonly coloredIcon = input<boolean>(false); readonly coloredIcon = input<boolean>(false);
/**
* Optional custom size for the icon in pixels.
* When provided, forces explicit dimensions on the icon wrapper to prevent layout collapse at different zoom levels.
* If not provided, the wrapper has no explicit dimensions and relies on CSS classes (tw-size-6/24px for images).
* This can cause the wrapper to collapse when images are loading/hidden, especially at high browser zoom levels.
* Reference: default image size is tw-size-6 (24px), coloredIcon uses 36px.
*/
readonly size = input<number>();
readonly imageLoaded = signal(false); readonly imageLoaded = signal(false);
/**
* Computed style object for icon dimensions.
* Centralizes the sizing logic to avoid repetition in the template.
*/
protected readonly iconStyle = computed(() => {
if (this.coloredIcon()) {
return { width: "36px", height: "36px" };
}
const size = this.size();
if (size) {
return { width: size + "px", height: size + "px" };
}
return {};
});
protected data$: Observable<CipherIconDetails>; protected data$: Observable<CipherIconDetails>;
constructor( constructor(