1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 14:23:32 +00:00

[CL-725] dialog adjustments (#15660)

* update dialog spacing/borders

* update scroll bottom threshold

* add shadow to dialog

* adjust simple dialog shadow

* update scroll util comment

* add background input to other dialog stories

* assign initial value to isScrollable

* add larger padding on alt background content

* update tab border color

* update tab content padding

* update tab content spacing

* revert tab border color change

* bring back tab border color changes

* update web header border to match new tab border

* add background to props in story
This commit is contained in:
Bryan Cunningham
2025-08-11 11:33:09 -04:00
committed by GitHub
parent d3c6ba9f4b
commit af12e376f7
9 changed files with 49 additions and 22 deletions

View File

@@ -2,7 +2,7 @@
*ngIf="routeData$ | async as routeData" *ngIf="routeData$ | async as routeData"
class="-tw-m-6 tw-mb-3 tw-flex tw-flex-col tw-p-6" class="-tw-m-6 tw-mb-3 tw-flex tw-flex-col tw-p-6"
[ngClass]="{ [ngClass]="{
'tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300 tw-bg-background-alt tw-pb-0': 'tw-border-0 tw-border-b tw-border-solid tw-border-secondary-100 tw-bg-background-alt tw-pb-0':
tabsContainer.childElementCount !== 0, tabsContainer.childElementCount !== 0,
}" }"
> >

View File

@@ -1,18 +1,18 @@
@let isDrawer = dialogRef?.isDrawer; @let isDrawer = dialogRef?.isDrawer;
<section <section
class="tw-flex tw-w-full tw-flex-col tw-self-center tw-overflow-hidden tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-text-main" class="tw-flex tw-w-full tw-flex-col tw-self-center tw-overflow-hidden tw-border tw-border-solid tw-border-secondary-100 tw-bg-background tw-text-main"
[ngClass]="[width, isDrawer ? 'tw-h-screen tw-border-t-0' : 'tw-rounded-xl']" [ngClass]="[width, isDrawer ? 'tw-h-screen tw-border-t-0' : 'tw-rounded-xl tw-shadow-lg']"
@fadeIn @fadeIn
cdkTrapFocus cdkTrapFocus
cdkTrapFocusAutoCapture cdkTrapFocusAutoCapture
> >
@let showHeaderBorder = !isDrawer || background() === "alt" || bodyHasScrolledFrom().top; @let showHeaderBorder = bodyHasScrolledFrom().top;
<header <header
class="tw-flex tw-justify-between tw-items-center tw-gap-4 tw-border-0 tw-border-b tw-border-solid" class="tw-flex tw-justify-between tw-items-center tw-gap-4 tw-border-0 tw-border-b tw-border-solid tw-py-3 tw-ps-6 tw-pe-4"
[ngClass]="{ [ngClass]="{
'tw-p-4 has-[[biticonbutton]]:tw-pe-2': !isDrawer, 'tw-p-4 has-[[biticonbutton]]:tw-pe-2': !isDrawer,
'tw-px-6 tw-py-4 has-[[biticonbutton]]:tw-pe-4': isDrawer, 'tw-px-6 tw-py-4 has-[[biticonbutton]]:tw-pe-4': isDrawer,
'tw-border-secondary-300': showHeaderBorder, 'tw-border-secondary-100': showHeaderBorder,
'tw-border-transparent': !showHeaderBorder, 'tw-border-transparent': !showHeaderBorder,
}" }"
> >
@@ -59,25 +59,25 @@
<div <div
cdkScrollable cdkScrollable
[ngClass]="{ [ngClass]="{
'tw-p-4': !disablePadding() && !isDrawer, 'tw-py-2 tw-ps-6 tw-pe-6': !disablePadding(),
'tw-px-6 tw-py-4': !disablePadding() && isDrawer,
'tw-overflow-y-auto': !loading(), 'tw-overflow-y-auto': !loading(),
'tw-invisible tw-overflow-y-hidden': loading(), 'tw-invisible tw-overflow-y-hidden': loading(),
'tw-py-4': background() === 'alt',
}" }"
> >
<ng-content select="[bitDialogContent]"></ng-content> <ng-content select="[bitDialogContent]"></ng-content>
</div> </div>
</div> </div>
@let showFooterBorder = !isDrawer || background() === "alt" || bodyHasScrolledFrom().bottom; @let showFooterBorder =
(!bodyHasScrolledFrom().top && isScrollable) || bodyHasScrolledFrom().bottom;
<footer <footer
class="tw-flex tw-flex-row tw-items-center tw-gap-2 tw-border-0 tw-border-t tw-border-solid tw-border-secondary-300 tw-bg-background" class="tw-flex tw-flex-row tw-items-center tw-gap-2 tw-border-0 tw-border-t tw-border-solid tw-bg-background tw-py-5 tw-ps-6 tw-pe-4"
[ngClass]="[isDrawer ? 'tw-px-6 tw-py-4' : 'tw-p-4']"
[ngClass]="{ [ngClass]="{
'tw-px-6 tw-py-4': isDrawer, 'tw-px-6 tw-py-4': isDrawer,
'tw-p-4 has-[[biticonbutton]]:tw-pe-2': !isDrawer, 'tw-p-4 has-[[biticonbutton]]:tw-pe-2': !isDrawer,
'tw-border-secondary-300': showFooterBorder,
'tw-border-transparent': !showFooterBorder, 'tw-border-transparent': !showFooterBorder,
'tw-border-secondary-100': showFooterBorder,
}" }"
> >
<ng-content select="[bitDialogFooter]"></ng-content> <ng-content select="[bitDialogFooter]"></ng-content>

View File

@@ -1,7 +1,15 @@
import { CdkTrapFocus } from "@angular/cdk/a11y"; import { CdkTrapFocus } from "@angular/cdk/a11y";
import { CdkScrollable } from "@angular/cdk/scrolling"; import { CdkScrollable } from "@angular/cdk/scrolling";
import { CommonModule } from "@angular/common"; import { CommonModule } from "@angular/common";
import { Component, HostBinding, inject, viewChild, input, booleanAttribute } from "@angular/core"; import {
Component,
HostBinding,
inject,
viewChild,
input,
booleanAttribute,
AfterViewInit,
} from "@angular/core";
import { I18nPipe } from "@bitwarden/ui-common"; import { I18nPipe } from "@bitwarden/ui-common";
@@ -31,10 +39,11 @@ import { DialogTitleContainerDirective } from "../directives/dialog-title-contai
CdkScrollable, CdkScrollable,
], ],
}) })
export class DialogComponent { export class DialogComponent implements AfterViewInit {
protected dialogRef = inject(DialogRef, { optional: true }); protected dialogRef = inject(DialogRef, { optional: true });
private scrollableBody = viewChild.required(CdkScrollable); private scrollableBody = viewChild.required(CdkScrollable);
protected bodyHasScrolledFrom = hasScrolledFrom(this.scrollableBody); protected bodyHasScrolledFrom = hasScrolledFrom(this.scrollableBody);
protected isScrollable = false;
/** Background color */ /** Background color */
readonly background = input<"default" | "alt">("default"); readonly background = input<"default" | "alt">("default");
@@ -96,4 +105,13 @@ export class DialogComponent {
} }
} }
} }
ngAfterViewInit() {
this.isScrollable = this.canScroll();
}
canScroll(): boolean {
const el = this.scrollableBody().getElementRef().nativeElement as HTMLElement;
return el.scrollHeight > el.clientHeight;
}
} }

View File

@@ -64,6 +64,13 @@ export default {
disable: true, disable: true,
}, },
}, },
background: {
options: ["alt", "default"],
control: { type: "radio" },
table: {
defaultValue: "default",
},
},
}, },
parameters: { parameters: {
design: { design: {
@@ -144,7 +151,7 @@ export const ScrollingContent: Story = {
render: (args) => ({ render: (args) => ({
props: args, props: args,
template: /*html*/ ` template: /*html*/ `
<bit-dialog title="Scrolling Example" [dialogSize]="dialogSize" [loading]="loading" [disablePadding]="disablePadding"> <bit-dialog title="Scrolling Example" [background]="background" [dialogSize]="dialogSize" [loading]="loading" [disablePadding]="disablePadding">
<span bitDialogContent> <span bitDialogContent>
Dialog body text goes here.<br /> Dialog body text goes here.<br />
<ng-container *ngFor="let _ of [].constructor(100)"> <ng-container *ngFor="let _ of [].constructor(100)">
@@ -168,7 +175,7 @@ export const TabContent: Story = {
render: (args) => ({ render: (args) => ({
props: args, props: args,
template: /*html*/ ` template: /*html*/ `
<bit-dialog title="Tab Content Example" [dialogSize]="dialogSize" [disablePadding]="disablePadding"> <bit-dialog title="Tab Content Example" [background]="background" [dialogSize]="dialogSize" [disablePadding]="disablePadding">
<span bitDialogContent> <span bitDialogContent>
<bit-tab-group> <bit-tab-group>
<bit-tab label="First Tab">First Tab Content</bit-tab> <bit-tab label="First Tab">First Tab Content</bit-tab>
@@ -205,7 +212,7 @@ export const WithCards: Story = {
}, },
template: /*html*/ ` template: /*html*/ `
<form [formGroup]="formObj"> <form [formGroup]="formObj">
<bit-dialog background="alt" [dialogSize]="dialogSize" [title]="title" [subtitle]="subtitle" [loading]="loading" [disablePadding]="disablePadding"> <bit-dialog [dialogSize]="dialogSize" [background]="background" [title]="title" [subtitle]="subtitle" [loading]="loading" [disablePadding]="disablePadding">
<ng-container bitDialogContent> <ng-container bitDialogContent>
<bit-section> <bit-section>
<bit-section-header> <bit-section-header>
@@ -269,5 +276,6 @@ export const WithCards: Story = {
dialogSize: "default", dialogSize: "default",
title: "Default", title: "Default",
subtitle: "Subtitle", subtitle: "Subtitle",
background: "alt",
}, },
}; };

View File

@@ -1,5 +1,5 @@
<div <div
class="tw-my-4 tw-pb-6 tw-pt-8 tw-flex tw-max-h-screen tw-w-96 tw-max-w-90vw tw-flex-col tw-overflow-hidden tw-rounded-3xl tw-border tw-border-solid tw-border-secondary-100 tw-shadow-xl tw-bg-text-contrast tw-text-main" class="tw-my-4 tw-pb-6 tw-pt-8 tw-flex tw-max-h-screen tw-w-96 tw-max-w-90vw tw-flex-col tw-overflow-hidden tw-rounded-3xl tw-border tw-border-solid tw-border-secondary-100 tw-shadow-lg tw-bg-text-contrast tw-text-main"
@fadeIn @fadeIn
> >
<div class="tw-flex tw-px-6 tw-flex-col tw-items-center tw-gap-2 tw-text-center"> <div class="tw-flex tw-px-6 tw-flex-col tw-items-center tw-gap-2 tw-text-center">

View File

@@ -7,7 +7,7 @@ import { Component } from "@angular/core";
selector: "bit-tab-header", selector: "bit-tab-header",
host: { host: {
class: class:
"tw-h-16 tw-ps-4 tw-bg-background-alt tw-flex tw-items-end tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300", "tw-h-16 tw-ps-4 tw-bg-background-alt tw-flex tw-items-end tw-border-0 tw-border-b tw-border-solid tw-border-secondary-100",
}, },
template: `<ng-content></ng-content>`, template: `<ng-content></ng-content>`,
}) })

View File

@@ -84,7 +84,7 @@ export class TabListItemDirective implements FocusableOption {
get activeClassList(): string[] { get activeClassList(): string[] {
return [ return [
"tw--mb-px", "tw--mb-px",
"tw-border-x-secondary-300", "tw-border-x-secondary-100",
"tw-border-t-primary-600", "tw-border-t-primary-600",
"tw-border-b", "tw-border-b",
"tw-border-b-background", "tw-border-b-background",

View File

@@ -29,7 +29,7 @@
} }
</div> </div>
</bit-tab-header> </bit-tab-header>
<div class="tw-px-4 tw-pt-5"> <div class="tw-px-6 tw-pt-5">
@for (tab of tabs; track tab; let i = $index) { @for (tab of tabs; track tab; let i = $index) {
<bit-tab-body <bit-tab-body
role="tabpanel" role="tabpanel"

View File

@@ -26,7 +26,8 @@ export const hasScrolledFrom = (scrollable?: Signal<CdkScrollable>): Signal<Scro
startWith(null), startWith(null),
map(() => ({ map(() => ({
top: _scrollable.measureScrollOffset("top") > 0, top: _scrollable.measureScrollOffset("top") > 0,
bottom: _scrollable.measureScrollOffset("bottom") > 0, // Using 1 as the threshold here because `_scrollable.measureScrollOffset("bottom")` returns '0.5' at the bottom most position in Chrome
bottom: _scrollable.measureScrollOffset("bottom") > 1,
})), })),
), ),
), ),