mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 06:13:38 +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:
@@ -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,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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>`,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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,
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user