Skip to content

Commit

Permalink
feat: add drawer component
Browse files Browse the repository at this point in the history
  • Loading branch information
zedix committed Jun 2, 2024
1 parent c1b6853 commit bd58015
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 1 deletion.
6 changes: 5 additions & 1 deletion src/components/dialog/dialog.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ export default class Dialog extends LitElement {

private readonly animations = new Map();

// https://chromestatus.com/feature/4722261258928128
// private closeWatcher: null;

@query('dialog') dialog!: HTMLDialogElement;

/**
Expand Down Expand Up @@ -173,6 +176,7 @@ export default class Dialog extends LitElement {
dispatchEvent(this, 'show');

this.lockBodyScroll();

// Dialog element must be rendered before any animate() call
this.dialog.showModal();

Expand Down Expand Up @@ -272,7 +276,7 @@ export default class Dialog extends LitElement {
window.scrollTo(0, Number(body.dataset.scrollY));
}

private setupDefaultAnimations() {
setupDefaultAnimations() {
// Default animations
this.setAnimation('dialog.show', {
keyframes: [
Expand Down
67 changes: 67 additions & 0 deletions src/components/drawer/drawer.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { CSSResultGroup, PropertyValues } from 'lit';
import { property } from 'lit/decorators.js';
import componentStyles from '../../styles/component.styles.js';
import Dialog from '../dialog/dialog';
import styles from './drawer.styles';

export default class Drawer extends Dialog {
static styles: CSSResultGroup = [
// Inherits styles from Dialog
// https://lit.dev/docs/components/styles/#inheriting-styles-from-a-superclass
//super.styles,
componentStyles,
styles,
];

@property({ reflect: true })
placement: 'end' | 'start' = 'start';

constructor() {
super();
this.setupDefaultAnimations();
}

async updated(changedProperties: PropertyValues<this>) {
await super.updated(changedProperties);

if (changedProperties.has('placement')) {
this.setupDefaultAnimations();
}
}

setupDefaultAnimations() {
if (this.placement === 'end') {
this.setAnimation('dialog.show', {
keyframes: [
{ opacity: 0, translate: '100%' },
{ opacity: 1, translate: '0' },
],
options: { duration: 250, easing: 'ease' },
});

this.setAnimation('dialog.close', {
keyframes: [
{ opacity: 1, translate: '0' },
{ opacity: 0, translate: '100%' },
],
options: { duration: 250, easing: 'ease' },
});
} else {
this.setAnimation('dialog.show', {
keyframes: [
{ opacity: 0, translate: '-100%' },
{ opacity: 1, translate: '0' },
],
options: { duration: 250, easing: 'ease' },
});

this.setAnimation('dialog.close', {
keyframes: [
{ opacity: 1, translate: '0' },
{ opacity: 0, translate: '-100%' },
],
options: { duration: 250, easing: 'ease' },
});
}
}
}
25 changes: 25 additions & 0 deletions src/components/drawer/drawer.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Meta, Primary, Controls, Source, Story, Canvas } from '@storybook/blocks';
import * as Stories from './drawer.stories';

<Meta title="Core/zx-drawer" />

# Drawer

`zx-drawer` is based on the [native HTML dialog element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog).

## Example

<Source
dark
code={`<zx-drawer placement="start">
#shadow-root (open)
<!-- use the native html dialog -->
<dialog>…</dialog>
<div>
Drawer content…
</div>
</zx-drawer>`}
/>

<Story of={Stories.Showcase} />
<Controls of={Stories.Showcase} />
66 changes: 66 additions & 0 deletions src/components/drawer/drawer.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { Meta, StoryObj } from '@storybook/web-components';
import { html } from 'lit';
import './drawer';

type Story = StoryObj;

const meta: Meta = {
title: 'Core/zx-drawer',
component: 'zx-drawer',
argTypes: {
placement: {
control: 'inline-radio',
options: ['start', 'end'],
},
},
parameters: {
actions: {
handles: ['show', 'close', 'after-close'],
},
},
args: {
open: false,
placement: 'start',
persistent: false,
quick: false,
noCloseButton: false,
noHeader: true,
},
};

function openDrawer() {
const drawer = document.querySelector('zx-drawer');
drawer!.show();
}

export const Showcase: Story = {
render: (args: any) => html`
<button
class="inline-flex items-center gap-2 border-2 rounded px-4 py-2 focus:ring-4"
type="button"
@click="${openDrawer}"
>
☰ Menu
</button>
<zx-drawer
.open="${args.open}"
.placement="${args.placement}"
.persistent="${args.persistent}"
.quick="${args.quick}"
.noCloseButton="${args.noCloseButton}"
.noHeader="${args.noHeader}"
>
${['Menu 1', 'Menu 2', 'Menu 3'].map(
menu => html`
<details name="menu">
<summary class="flex items-center p-4 border-b">${menu}</summary>
<div class="flex p-4">Sub menu</div>
</details>
`,
)}
</zx-drawer>
`,
};

export default meta;
62 changes: 62 additions & 0 deletions src/components/drawer/drawer.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { css } from 'lit';

export default css`
:host {
--width: 320px;
--backdrop: hsl(240 3.8% 46.1% / 33%);
--border-radius: 0.75rem;
--transition-duration: 250ms;
--padding: 0;
}
dialog {
/* position: absolute to be contained by its relative parent */
position: fixed;
width: min(var(--width), 100dvw - 2rem);
box-sizing: border-box;
height: 100dvh;
max-height: unset;
padding: var(--padding);
border: none;
box-shadow: none;
/*
transition-property: translate, overlay, display;
transition-behavior: allow-discrete;
transition-duration: var(--transition-duration);
translate: -100% 0;
*/
}
dialog:not([open]) {
display: none;
}
dialog::backdrop {
background: var(--backdrop);
}
:host([placement='start']) dialog {
left: 0;
margin-left: 0;
margin-right: auto;
border-radius: 0 var(--border-radius) var(--border-radius) 0;
}
:host([placement='end']) dialog {
right: 0;
margin-right: 0;
margin-left: auto;
border-radius: var(--border-radius) 0 0 var(--border-radius);
}
.dialog__close-button {
position: absolute;
cursor: pointer;
right: 0px;
top: 0px;
z-index: 1;
user-select: none;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
}
`;
14 changes: 14 additions & 0 deletions src/components/drawer/drawer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Drawer from './drawer.component';

export * from './drawer.component';
export default Drawer;

if (!customElements.get('zx-drawer')) {
customElements.define('zx-drawer', Drawer);
}

declare global {
interface HTMLElementTagNameMap {
'zx-drawer': Drawer;
}
}

0 comments on commit bd58015

Please sign in to comment.