Skip to content

Commit

Permalink
feat(dialog): restructure JSX and add esc key support
Browse files Browse the repository at this point in the history
  • Loading branch information
endv-bogdanb committed Jun 19, 2023
1 parent 7849293 commit d028bd7
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 89 deletions.
8 changes: 8 additions & 0 deletions packages/bee-q/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ export namespace Components {
* Hides the dialog
*/
"close": () => Promise<void>;
/**
* If true will not close on escape press
*/
"disableEscKeyDownClose": boolean;
/**
* If true will not close on outside click
*/
Expand Down Expand Up @@ -909,6 +913,10 @@ declare namespace LocalJSX {
"value": string;
}
interface BqDialog {
/**
* If true will not close on escape press
*/
"disableEscKeyDownClose"?: boolean;
/**
* If true will not close on outside click
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const meta: Meta = {
'footer-apperance': { control: 'select', options: [...DIALOG_FOOTER_APPEARANCE] },
'hide-close-button': { control: 'boolean' },
'disable-outside-click-close': { control: 'boolean' },
'disable-esc-key-down-close': { control: 'boolean' },
},
args: {
text: 'text',
Expand All @@ -44,22 +45,21 @@ const Template = (args: Args) => {
footer-apperance=${args['footer-apperance']}
?hide-close-button=${args['hide-close-button']}
?disable-outside-click-close=${args['disable-outside-click-close']}
?disable-esc-key-down-close=${args['disable-esc-key-down-close']}
>
<div slot="icon">
<bq-icon name="info" color="text--accent" role="img" title="Info" part="icon-on" />
</div>
<bq-icon name="info" color="text--accent" role="img" title="Info" part="icon-on" slot="icon"></bq-icon>
<h3 slot="title">Title</h3>
<p slot="content">
<p slot="description">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's
standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make
a type specimen book.
</p>
<footer slot="footer">
<bq-button appearance="primary" size="medium" type="button" variant="ghost" class="hydrated">
Ghost button
</bq-button>
<bq-button appearance="primary" size="medium" type="button" variant="standard"> Standard button </bq-button>
</footer>
<bq-button appearance="primary" size="medium" type="button" variant="ghost" class="hydrated" slot="footer">
Ghost button
</bq-button>
<bq-button appearance="primary" size="medium" type="button" variant="standard" slot="footer">
Standard button
</bq-button>
</bq-dialog>
`;
};
Expand All @@ -86,31 +86,32 @@ const ConfirmTemplate = (args: Args) => {
footer-apperance=${args['footer-apperance']}
?hide-close-button=${args['hide-close-button']}
?disable-outside-click-close=${args['disable-outside-click-close']}
?disable-esc-key-down-close=${args['disable-esc-key-down-close']}
>
<h3 slot="title">Lorem Ipsum ?</h3>
<p slot="content">Lorem Ipsum is simply dummy text of the printing and typesetting industry.</p>
<footer slot="footer">
<bq-button
appearance="primary"
size="medium"
type="button"
variant="ghost"
class="hydrated"
@bqClick=${handleCloseDialog}
>
Disagree
</bq-button>
<bq-button
appearance="primary"
size="medium"
type="button"
variant="ghost"
class="hydrated"
@bqClick=${handleCloseDialog}
>
Agree
</bq-button>
</footer>
<p slot="description">Lorem Ipsum is simply dummy text of the printing and typesetting industry.</p>
<bq-button
appearance="primary"
size="medium"
type="button"
variant="ghost"
class="hydrated"
@bqClick=${handleCloseDialog}
slot="footer"
>
Disagree
</bq-button>
<bq-button
appearance="primary"
size="medium"
type="button"
variant="standard"
class="hydrated"
@bqClick=${handleCloseDialog}
slot="footer"
>
Agree
</bq-button>
</bq-dialog>
`;
};
Expand All @@ -120,5 +121,6 @@ export const Confirm: Story = {
args: {
'hide-close-button': true,
'disable-outside-click-close': true,
'disable-esc-key-down-close': true,
},
};
107 changes: 70 additions & 37 deletions packages/bee-q/src/components/dialog/bq-dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { h, Component, Prop, Element, Watch, State, Method, Host } from '@stencil/core';
import { h, Component, Prop, Element, Watch, State, Method, Host, Listen } from '@stencil/core';

import { DIALOG_SIZE, TDialogSize, TDialogFooterAppearance, DIALOG_FOOTER_APPEARANCE } from './bq-dialog.types';
import { hasSlotContent, validatePropValue } from '../../shared/utils';

/**
* @part base - The component wrapper container inside the shadow DOM
* @part backdrop - The `<div>` that displays the background
* @part container - The `<div>` container that holds the dialog content
* @part header - The `<header>` that holds the icon, title, description and close button
* @part icon - The `<div>` that holds the info icon
* @part title - The `<div>` that holds the title content
* @part button-close - The button that close the dialog on click
* @part description- The `<div>` that holds the description content
* @part content- The `<main>` that holds the content
* @part footer - The `<footer>` that holds footer content
*/

Expand All @@ -21,7 +26,9 @@ export class BqDialog {
// ====================

private overlayElem: HTMLDivElement;
private iconElem: HTMLElement;
private iconElem: HTMLDivElement;
private contentElem: HTMLElement;
private footerElem: HTMLElement;

// Reference to host HTML element
// ===================================
Expand All @@ -33,7 +40,11 @@ export class BqDialog {

@State() private isOpen = false;

@State() private hasIconIcon = false;
@State() private hasIcon = false;

@State() private hasContent = false;

@State() private hasFooter = false;

// Public Property API
// ========================
Expand All @@ -50,6 +61,9 @@ export class BqDialog {
/** If true will not close on outside click */
@Prop({ reflect: true }) disableOutsideClickClose = false;

/** If true will not close on escape press */
@Prop({ reflect: true }) disableEscKeyDownClose = false;

// Prop lifecycle events
// =======================
@Watch('size')
Expand All @@ -74,6 +88,15 @@ export class BqDialog {
// Listeners
// ==============

@Listen('keydown', { target: 'body', passive: true })
async onKeyDown(event: KeyboardEvent) {
if (!this.open || this.disableEscKeyDownClose) return;

if (event.key === 'Escape') {
await this.close();
}
}

// Public methods API
// These methods are exposed on the host element.
// Always use two lines.
Expand All @@ -98,17 +121,26 @@ export class BqDialog {
// These methods cannot be called from the host element.
// =======================================================

handleCloseClick = () => {
private handleOverlayClick = (event: MouseEvent) => {
if (event.target !== this.overlayElem || this.disableOutsideClickClose) return;

this.isOpen = false;
};

handleOverlayClick = (event: MouseEvent) => {
if (event.target !== this.overlayElem || this.disableOutsideClickClose) return;
private handleCloseClick = () => {
this.isOpen = false;
};

private handleIconSlotChange = () => {
this.hasIconIcon = hasSlotContent(this.iconElem, 'icon');
this.hasIcon = hasSlotContent(this.iconElem, 'icon');
};

private handleContentSlotChange = () => {
this.hasContent = hasSlotContent(this.contentElem, 'content');
};

private handleFooterSlotChange = () => {
this.hasFooter = hasSlotContent(this.footerElem, 'footer');
};

// render() function
Expand All @@ -119,49 +151,50 @@ export class BqDialog {
return (
<Host class={{ 'is-open': this.isOpen }} part="base">
<div
class="overlay fixed h-full w-full bg-bg-tertiary opacity-75"
class="bq-dialog__backdrop"
ref={(divElem) => (this.overlayElem = divElem)}
onClick={this.handleOverlayClick}
ref={(el) => (this.overlayElem = el)}
part="backdrop"
/>
<div
class={{
'z-10 m-auto flex flex-col rounded-s bg-bg-primary shadow-m': true,
[`bq-dialog__size--${this.size}`]: true,
}}
part="container"
>
<header class="bq-dialog__header">
<div
class={{ 'bq-dialog__icon': this.hasIconIcon }}
ref={(divElem) => (this.iconElem = divElem)}
part="icon"
>
<div class={{ [`bq-dialog__container bq-dialog__container--${this.size}`]: true }} part="container">
<header class="bq-dialog__header" part="header">
<div class={{ 'bq-dialog__icon': this.hasIcon }} ref={(divElem) => (this.iconElem = divElem)} part="icon">
<slot name="icon" onSlotchange={this.handleIconSlotChange} />
</div>
<div class="flex flex-col pl-m">
<slot name="title" />
<div class="bq-description">
<slot name="content" />
<div class="flex flex-col gap-s">
<div class="bq-dialog__title" part="title">
<slot name="title" />
<div part="button-close" class="self-baseline" onClick={this.handleCloseClick}>
<slot name="button-close">
{!this.hideCloseButton && (
<bq-button class="bq-dialog__button-close" appearance="text" size="small" slot="button-close">
<bq-icon class="cursor-pointer" name="x" role="img" title="Close" />
</bq-button>
)}
</slot>
</div>
</div>
<div part="description">
<slot name="description" />
</div>
</div>
<div part="button-close" onClick={this.handleCloseClick}>
<slot name="button-close">
{!this.hideCloseButton && (
<bq-button class="cursor-auto" appearance="text" size="small" slot="button-close">
<bq-icon class="cursor-pointer" name="x" role="img" title="Close" />
</bq-button>
)}
</slot>
</div>
</header>
<main
class={{ hidden: !this.hasContent, 'px-l': this.hasContent }}
ref={(mainElem) => (this.contentElem = mainElem)}
part="content"
>
<slot name="content" onSlotchange={this.handleContentSlotChange} />
</main>
<footer
class={{
'flex w-full items-center justify-end px-l py-s': true,
'bq-dialog__footer': this.hasFooter,
'bg-ui-secondary-light': this.footerApperance === 'highlight',
}}
ref={(footerElem) => (this.footerElem = footerElem)}
part="footer"
>
<slot name="footer" />
<slot name="footer" onSlotchange={this.handleFooterSlotChange} />
</footer>
</div>
</Host>
Expand Down
22 changes: 15 additions & 7 deletions packages/bee-q/src/components/dialog/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

| Property | Attribute | Description | Type | Default |
| -------------------------- | ----------------------------- | --------------------------------------- | -------------------------------- | ------------ |
| `disableEscKeyDownClose` | `disable-esc-key-down-close` | If true will not close on escape press | `boolean` | `false` |
| `disableOutsideClickClose` | `disable-outside-click-close` | If true will not close on outside click | `boolean` | `false` |
| `footerApperance` | `footer-apperance` | The appearance of footer | `"highlight" \| "standard"` | `'standard'` |
| `hideCloseButton` | `hide-close-button` | If true it hides close button | `boolean` | `false` |
Expand Down Expand Up @@ -40,13 +41,20 @@ Type: `Promise<void>`

## Shadow Parts

| Part | Description |
| ---------------- | ----------------------------------------------------- |
| `"base"` | The component wrapper container inside the shadow DOM |
| `"button-close"` | The button that close the dialog on click |
| `"container"` | The `<div>` container that holds the dialog content |
| `"footer"` | The `<footer>` that holds footer content |
| `"icon"` | The `<div>` that holds the info icon |
| Part | Description |
| --------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `"backdrop"` | The `<div>` that displays the background |
| `"base"` | The component wrapper container inside the shadow DOM |
| `"button-close"` | The button that close the dialog on click |
| `"container"` | The `<div>` container that holds the dialog content |
| `"content"` | |
| `"content- The `<main>` that holds the content"` | |
| `"description"` | |
| `"description- The `<div>` that holds the description content"` | |
| `"footer"` | The `<footer>` that holds footer content |
| `"header"` | The `<header>` that holds the icon, title, description and close button |
| `"icon"` | The `<div>` that holds the info icon |
| `"title"` | The `<div>` that holds the title content |


## Dependencies
Expand Down
Loading

0 comments on commit d028bd7

Please sign in to comment.