mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-21 21:11:35 +01:00
[CL-29] Modal Components for the component library (#3053)
The purpose of this PR is to add Modals into the component library. To note, this PR is focused on the visual of the modal that will "pop-up" and not the actual pop-up implementation.
This commit is contained in:
parent
9fef1d21a4
commit
8cb92d425c
@ -5,6 +5,7 @@ export * from "./toggle-group";
|
|||||||
export * from "./callout";
|
export * from "./callout";
|
||||||
export * from "./form-field";
|
export * from "./form-field";
|
||||||
export * from "./menu";
|
export * from "./menu";
|
||||||
|
export * from "./modal";
|
||||||
export * from "./utils/i18n-mock.service";
|
export * from "./utils/i18n-mock.service";
|
||||||
export * from "./tabs";
|
export * from "./tabs";
|
||||||
export * from "./submit-button";
|
export * from "./submit-button";
|
||||||
|
3
libs/components/src/modal/index.ts
Normal file
3
libs/components/src/modal/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from "./modal.component";
|
||||||
|
export * from "./modal-simple.component";
|
||||||
|
export * from "./modal.module";
|
19
libs/components/src/modal/modal-simple.component.html
Normal file
19
libs/components/src/modal/modal-simple.component.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<div
|
||||||
|
class="tw-max-w-sm tw-max-h-screen tw-my-4 tw-flex tw-flex-col tw-bg-text-contrast tw-border tw-border-solid tw-border-secondary-300 tw-rounded tw-text-main tw-overflow-hidden"
|
||||||
|
>
|
||||||
|
<div class="tw-text-center tw-flex tw-flex-col tw-items-center tw-px-4 tw-pt-4 tw-gap-2">
|
||||||
|
<ng-content *ngIf="hasIcon; else elseBlock" select="[bit-modal-icon]"></ng-content>
|
||||||
|
<ng-template #elseBlock>
|
||||||
|
<i class="bwi bwi-exclamation-triangle tw-text-3xl tw-text-warning" aria-hidden="true"></i>
|
||||||
|
</ng-template>
|
||||||
|
<h2 class="tw-font-semibold tw-text-base tw-mb-0">
|
||||||
|
<ng-content select="[bit-modal-title]"></ng-content>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="tw-text-center tw-text-base tw-pt-2 tw-pb-4 tw-px-4 tw-overflow-y-auto">
|
||||||
|
<ng-content select="[bit-modal-content]"></ng-content>
|
||||||
|
</div>
|
||||||
|
<div class="tw-p-4 tw-border-solid tw-border-secondary-300 tw-border-0 tw-border-t">
|
||||||
|
<ng-content select="[bit-modal-footer]"></ng-content>
|
||||||
|
</div>
|
||||||
|
</div>
|
16
libs/components/src/modal/modal-simple.component.ts
Normal file
16
libs/components/src/modal/modal-simple.component.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { Component, ContentChild, Directive } from "@angular/core";
|
||||||
|
|
||||||
|
@Directive({ selector: "[bit-modal-icon]" })
|
||||||
|
export class IconDirective {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "bit-simple-modal",
|
||||||
|
templateUrl: "./modal-simple.component.html",
|
||||||
|
})
|
||||||
|
export class ModalSimpleComponent {
|
||||||
|
@ContentChild(IconDirective) icon!: IconDirective;
|
||||||
|
|
||||||
|
get hasIcon() {
|
||||||
|
return this.icon != null;
|
||||||
|
}
|
||||||
|
}
|
85
libs/components/src/modal/modal-simple.stories.ts
Normal file
85
libs/components/src/modal/modal-simple.stories.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import { Meta, moduleMetadata, Story } from "@storybook/angular";
|
||||||
|
|
||||||
|
import { ButtonModule } from "../button";
|
||||||
|
|
||||||
|
import { IconDirective, ModalSimpleComponent } from "./modal-simple.component";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Component Library/Modals/Simple Modal",
|
||||||
|
component: ModalSimpleComponent,
|
||||||
|
decorators: [
|
||||||
|
moduleMetadata({
|
||||||
|
imports: [ButtonModule],
|
||||||
|
declarations: [IconDirective],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
parameters: {
|
||||||
|
design: {
|
||||||
|
type: "figma",
|
||||||
|
url: "https://www.figma.com/file/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as Meta;
|
||||||
|
|
||||||
|
const Template: Story<ModalSimpleComponent> = (args: ModalSimpleComponent) => ({
|
||||||
|
props: args,
|
||||||
|
template: `
|
||||||
|
<bit-simple-modal>
|
||||||
|
<span bit-modal-title> Alert Modal
|
||||||
|
</span>
|
||||||
|
<span bit-modal-content> Message Content
|
||||||
|
</span>
|
||||||
|
<div bit-modal-footer class="tw-flex tw-flex-row tw-gap-2">
|
||||||
|
<button bitButton buttonType="primary"> Yes </button>
|
||||||
|
<button bitButton buttonType="secondary"> No </button>
|
||||||
|
</div>
|
||||||
|
</bit-simple-modal>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
|
||||||
|
const TemplateWithIcon: Story<ModalSimpleComponent> = (args: ModalSimpleComponent) => ({
|
||||||
|
props: args,
|
||||||
|
template: `
|
||||||
|
<bit-simple-modal>
|
||||||
|
<i bit-modal-icon class="bwi bwi-star tw-text-3xl tw-text-success" aria-hidden="true"></i>
|
||||||
|
<span bit-modal-title> Premium Subscription Available
|
||||||
|
</span>
|
||||||
|
<span bit-modal-content> Message Content
|
||||||
|
</span>
|
||||||
|
<div bit-modal-footer class="tw-flex tw-flex-row tw-gap-2">
|
||||||
|
<button bitButton buttonType="primary"> Yes </button>
|
||||||
|
<button bitButton buttonType="secondary"> No </button>
|
||||||
|
</div>
|
||||||
|
</bit-simple-modal>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CustomIcon = TemplateWithIcon.bind({});
|
||||||
|
|
||||||
|
const TemplateScroll: Story<ModalSimpleComponent> = (args: ModalSimpleComponent) => ({
|
||||||
|
props: args,
|
||||||
|
template: `
|
||||||
|
<bit-simple-modal>
|
||||||
|
<span bit-modal-title> Alert Modal
|
||||||
|
</span>
|
||||||
|
<span bit-modal-content> Message Content
|
||||||
|
Message text goes here.<br>
|
||||||
|
<ng-container *ngFor="let _ of [].constructor(100)">
|
||||||
|
repeating lines of characters <br>
|
||||||
|
</ng-container>
|
||||||
|
end of sequence!
|
||||||
|
</span>
|
||||||
|
<div bit-modal-footer class="tw-flex tw-flex-row tw-gap-2">
|
||||||
|
<button bitButton buttonType="primary"> Yes </button>
|
||||||
|
<button bitButton buttonType="secondary"> No </button>
|
||||||
|
</div>
|
||||||
|
</bit-simple-modal>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ScrollingContent = TemplateScroll.bind({});
|
||||||
|
ScrollingContent.args = {
|
||||||
|
useDefaultIcon: true,
|
||||||
|
};
|
25
libs/components/src/modal/modal.component.html
Normal file
25
libs/components/src/modal/modal.component.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<div
|
||||||
|
[ngClass]="width"
|
||||||
|
class="tw-max-h-screen tw-my-4 tw-flex tw-flex-col tw-bg-text-contrast tw-border tw-border-solid tw-border-secondary-300 tw-rounded tw-text-main tw-overflow-hidden"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="tw-flex tw-gap-4 tw-p-4 tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300"
|
||||||
|
>
|
||||||
|
<h2 class="tw-text-lg tw-uppercase tw-grow tw-mb-0">
|
||||||
|
<ng-content select="[bit-modal-title]"></ng-content>
|
||||||
|
</h2>
|
||||||
|
<button class="tw-border-0 tw-bg-transparent tw-p-0">
|
||||||
|
<i class="bwi bwi-close tw-text-xs tw-font-bold tw-text-main" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tw-pb-8 tw-p-4 tw-overflow-y-auto">
|
||||||
|
<ng-content select="[bit-modal-content]"></ng-content>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="tw-p-4 tw-bg-background-alt tw-border-solid tw-border-secondary-300 tw-border-0 tw-border-t"
|
||||||
|
>
|
||||||
|
<ng-content select="[bit-modal-footer]"></ng-content>
|
||||||
|
</div>
|
||||||
|
</div>
|
23
libs/components/src/modal/modal.component.ts
Normal file
23
libs/components/src/modal/modal.component.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Component, Input } from "@angular/core";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "bit-modal",
|
||||||
|
templateUrl: "./modal.component.html",
|
||||||
|
})
|
||||||
|
export class ModalComponent {
|
||||||
|
@Input() modalSize: "small" | "default" | "large";
|
||||||
|
|
||||||
|
get width() {
|
||||||
|
switch (this.modalSize) {
|
||||||
|
case "small": {
|
||||||
|
return "tw-max-w-xs";
|
||||||
|
}
|
||||||
|
case "large": {
|
||||||
|
return "tw-max-w-4xl";
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return "tw-max-w-xl";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
libs/components/src/modal/modal.module.ts
Normal file
12
libs/components/src/modal/modal.module.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { NgModule } from "@angular/core";
|
||||||
|
|
||||||
|
import { ModalSimpleComponent } from "./modal-simple.component";
|
||||||
|
import { ModalComponent } from "./modal.component";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
exports: [ModalComponent, ModalSimpleComponent],
|
||||||
|
declarations: [ModalComponent, ModalSimpleComponent],
|
||||||
|
})
|
||||||
|
export class ModalModule {}
|
80
libs/components/src/modal/modal.stories.ts
Normal file
80
libs/components/src/modal/modal.stories.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { Meta, moduleMetadata, Story } from "@storybook/angular";
|
||||||
|
|
||||||
|
import { ButtonModule } from "../button";
|
||||||
|
|
||||||
|
import { ModalComponent } from "./modal.component";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Component Library/Modals/Modal",
|
||||||
|
component: ModalComponent,
|
||||||
|
decorators: [
|
||||||
|
moduleMetadata({
|
||||||
|
imports: [ButtonModule],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
args: {
|
||||||
|
modalSize: "small",
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
design: {
|
||||||
|
type: "figma",
|
||||||
|
url: "https://www.figma.com/file/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as Meta;
|
||||||
|
|
||||||
|
const Template: Story<ModalComponent> = (args: ModalComponent) => ({
|
||||||
|
props: args,
|
||||||
|
template: `
|
||||||
|
<bit-modal [modalSize]="modalSize">
|
||||||
|
<span bit-modal-title> Modal Title </span>
|
||||||
|
<span bit-modal-content>
|
||||||
|
Modal body text goes here.
|
||||||
|
</span>
|
||||||
|
<div bit-modal-footer class="tw-flex tw-flex-row tw-gap-2">
|
||||||
|
<button bitButton buttonType="primary"> Save </button>
|
||||||
|
<button bitButton buttonType="secondary"> Cancel </button>
|
||||||
|
</div>
|
||||||
|
</bit-modal>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
Default.args = {
|
||||||
|
modalSize: "default",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Small = Template.bind({});
|
||||||
|
Small.args = {
|
||||||
|
modalSize: "small",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Large = Template.bind({});
|
||||||
|
Large.args = {
|
||||||
|
modalSize: "large",
|
||||||
|
};
|
||||||
|
|
||||||
|
const TemplateScrolling: Story<ModalComponent> = (args: ModalComponent) => ({
|
||||||
|
props: args,
|
||||||
|
template: `
|
||||||
|
<bit-modal [modalSize]="modalSize">
|
||||||
|
<span bit-modal-title> Modal Title </span>
|
||||||
|
<span bit-modal-content>
|
||||||
|
Modal body text goes here.<br>
|
||||||
|
<ng-container *ngFor="let _ of [].constructor(100)">
|
||||||
|
repeating lines of characters <br>
|
||||||
|
</ng-container>
|
||||||
|
end of sequence!
|
||||||
|
</span>
|
||||||
|
<div bit-modal-footer class="tw-flex tw-flex-row tw-gap-2">
|
||||||
|
<button bitButton buttonType="primary"> Save </button>
|
||||||
|
<button bitButton buttonType="secondary"> Cancel </button>
|
||||||
|
</div>
|
||||||
|
</bit-modal>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ScrollingContent = TemplateScrolling.bind({});
|
||||||
|
ScrollingContent.args = {
|
||||||
|
modalSize: "small",
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user