Skip to content

Commit

Permalink
add avatar with dropdown menu
Browse files Browse the repository at this point in the history
  • Loading branch information
mucsi96 committed May 3, 2024
1 parent ddff9f2 commit a1913f1
Show file tree
Hide file tree
Showing 14 changed files with 310 additions and 10 deletions.
36 changes: 36 additions & 0 deletions src/Avatar/Avatar.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { Meta, StoryObj } from '@storybook/web-components';
import { html } from 'lit';
import { BTAvatarProps } from './Avatar';

const meta: Meta<BTAvatarProps> = {
title: 'BTAvatar',
render: () =>
html`<header is="bt-header">
<nav>
<a href="/"><h1 is="bt-heading">App</h1></a>
<section>
<button is="bt-avatar" id="avatar" popovertarget="avatar-popover">
JD
</button>
<div popover id="avatar-popover" is="bt-popover">
<ul is="bt-dropdown-menu">
<li separated>
<p>John Doe</p>
<p>[email protected]</p>
</li>
<li><button type="button">Sign out</button></li>
</ul>
</div>
</section>
</nav>
</header>
<div style="min-height: 50px"></div>`,
parameters: {
layout: 'fullscreen',
},
};
type Story = StoryObj<BTAvatarProps>;

export default meta;

export const Default: Story = {};
26 changes: 26 additions & 0 deletions src/Avatar/Avatar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { css } from 'lit';
import { customElement } from '../utils';

export type BTAvatarProps = {};

@customElement({
name: 'bt-avatar',
extends: 'button',
styles: css`
:host {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
overflow: hidden;
background-color: hsl(215, 14%, 34%);
border-radius: 9999px;
border: none;
color: hsl(216, 12%, 84%);
cursor: pointer;
}
`,
})
export class BTAvatar extends HTMLButtonElement {}
15 changes: 15 additions & 0 deletions src/Docs.mdx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { Canvas, Meta } from '@storybook/blocks';

import * as AvatarStories from './Avatar/Avatar.stories';
import * as HeadingStories from './Heading/Heading.stories';
import * as BadgeStories from './Badge/Badge.stories';
import * as ButtonStories from './Button/Button.stories';
import * as DropdownMenuStories from './DropdownMenu/DropdownMenu.stories'
import * as HeaderStories from './Header/Header.stories';
import * as InputLabelStories from './InputLabel/InputLabel.stories';
import * as LoaderStories from './Loader/Loader.stories';
import * as MainStories from './Main/Main.stories';
import * as NotificationStories from './Notifications/Notifications.stories';
import * as PopoverStories from './Popover/Popover.stories';
import * as TableStories from './Table/Table.stories';

<Meta title="" />
Expand All @@ -33,6 +36,10 @@ body {
}
```

# Avatar

<Canvas of={AvatarStories.Default} />

# Badge

<Canvas of={BadgeStories.Normal} />
Expand All @@ -44,6 +51,10 @@ body {
<Canvas of={ButtonStories.Red} />
<Canvas of={ButtonStories.Yellow} />

# DropdownMenu

<Canvas of={DropdownMenuStories.Default} />

# Header

<Canvas of={HeaderStories.Default} />
Expand Down Expand Up @@ -87,6 +98,10 @@ document.dispatchEvent(new SuccessNotificationEvent('Completed'));
document.dispatchEvent(new ErrorNotificationEvent('Failure'));
```

# Popover

<Canvas of={PopoverStories.Default} />

# Table

<Canvas of={TableStories.Default} />
Expand Down
25 changes: 25 additions & 0 deletions src/DropdownMenu/DropdownMenu.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Meta, StoryObj } from '@storybook/web-components';
import { html } from 'lit';
import { BTDropdownMenuProps } from './DropdownMenu';

const meta: Meta<BTDropdownMenuProps> = {
title: 'BTDropdownMenu',
render: () =>
html`<div is="bt-popover">
<ul is="bt-dropdown-menu">
<li separated>
<p>John Doe</p>
<p>[email protected]</p>
</li>
<li><a href="#">Menu item 1</a></li>
<li><a href="#">Menu item 2</a></li>
<li separated><a href="#">Menu item 3</a></li>
<li><button type="button">Sign out</button></li>
</ul>
</div>`,
};
type Story = StoryObj<BTDropdownMenuProps>;

export default meta;

export const Default: Story = {};
88 changes: 88 additions & 0 deletions src/DropdownMenu/DropdownMenu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { css } from 'lit';
import { customElement } from '../utils';

export type BTDropdownMenuProps = {};

@customElement({
name: 'bt-dropdown-menu',
extends: 'ul',
styles: css`
:host {
list-style-type: unset;
margin: unset;
padding: unset;
li {
display: block;
font-size: 0.875rem;
color: hsl(220, 13%, 91%);
--padding: var(--padding-top, 0.5rem) 1rem var(--padding-bottom, 0.5rem);
--separator-gap: calc(0.3rem + 1px);
&:has(a, button):hover {
background-color: hsl(215, 14%, 34%);
color: var(--bt-strong-text-color);
}
&:not(:has(a, button)) {
padding: var(--padding);
}
&:first-child {
--padding-top: 0.75rem;
}
&:last-child {
--padding-bottom: 0.75rem;
}
&[separated] {
position: relative;
margin-bottom: var(--separator-gap);
&:after {
position: absolute;
height: 0.3rem;
bottom: calc(-0.15rem - 1px);
left: 0;
right: 0;
border-bottom: 1px solid hsla(215, 14%, 34%);
content: '';
display: block;
}
}
[separated] + & {
margin-top: var(--separator-gap);
}
}
a,
button {
padding: var(--padding);
}
a {
display: block;
text-decoration: unset;
color: inherit;
}
button {
width: 100%;
border: unset;
background-color: unset;
font: inherit;
cursor: pointer;
text-align: left;
color: inherit;
}
p {
margin: unset;
padding: 0.2rem 0;
}
}
`,
})
export class BTDropdownMenu extends HTMLUListElement {}
7 changes: 4 additions & 3 deletions src/Header/Header.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ const meta: Meta<BTHeaderProps> = {
<button is="bt-button">Sign in</button>
</nav>
</header>
<main is="bt-main">
<h1 is="bt-heading">main text</h1>
</main>`,
<div style="min-height: 50px"></div>`,
parameters: {
layout: 'fullscreen',
},
};
type Story = StoryObj<BTHeaderProps>;

Expand Down
2 changes: 1 addition & 1 deletion src/Header/Header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export type BTHeaderProps = {
font-family: var(--bt-font-family);
> * {
padding: 18px 1rem 19px;
padding: 0.75rem 1rem;
max-width: 90rem;
margin: 0 auto;
display: flex;
Expand Down
2 changes: 1 addition & 1 deletion src/Heading/Heading.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export type BTHeadingProps = {};
}
a:has(&):hover {
--heading-color: var(--bt-text-color);
--heading-color: var(--bt-link-hover-color);
}
}
`,
Expand Down
3 changes: 3 additions & 0 deletions src/Main/Main.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { BTMainProps } from './Main';
const meta: Meta<BTMainProps> = {
title: 'BTMain',
render: () => html`<main is="bt-main"><h1 is="bt-heading">Heading in main</h1></main>`,
parameters: {
layout: 'fullscreen',
}
};
type Story = StoryObj<BTMainProps>;

Expand Down
3 changes: 2 additions & 1 deletion src/Main/Main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ export type BTMainProps = {};
extends: 'main',
styles: css`
:root {
--bt-background-color: hsl(222, 47%, 11%);
--bt-background-color: hsl(221, 39%, 11%);
--bt-text-color: hsl(218, 11%, 65%);
--bt-strong-text-color: white;
--bt-font-family: system-ui;
--bt-link-hover-color: hsl(218, 93%, 61%);
}
:host {
Expand Down
5 changes: 2 additions & 3 deletions src/Notifications/Notifications.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ export default meta;

export const Default: Story = {
render: () =>
html`<div style="min-height: 300px;">
<bt-notifications-docs></bt-notifications-docs>
html` <bt-notifications-docs></bt-notifications-docs>
<section is="bt-notifications"></section>
</div>`,
<div style="min-height: 300px;"></div>`,
};
20 changes: 20 additions & 0 deletions src/Popover/Popover.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { Meta, StoryObj } from '@storybook/web-components';
import { html } from 'lit';
import { BTPopoverProps } from './Popover';

const meta: Meta<BTPopoverProps> = {
title: 'BTPopover',
render: () =>
html`<button is="bt-button" id="trigger" popovertarget="test-popover">
Open
</button>
<div popover id="test-popover" is="bt-popover">
<div>Content text</div>
</div>
<div style="min-height: 50px"></div>`,
};
type Story = StoryObj<BTPopoverProps>;

export default meta;

export const Default: Story = {};
83 changes: 83 additions & 0 deletions src/Popover/Popover.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { css } from 'lit';
import { customElement } from '../utils';

export type BTPopoverProps = {};

@customElement({
name: 'bt-popover',
extends: 'div',
styles: css`
:host {
margin: unset;
padding: unset;
white-space: nowrap;
top: calc(var(--trigger-bottom) + 0.5rem);
background-color: hsl(217, 19%, 27%);
border-radius: 0.5rem;
min-width: 11rem;
border: none;
color: var(--bt-strong-text-color);
overflow: hidden;
&.left {
left: var(--trigger-left);
}
&.right {
left: var(--trigger-right);
transform: translateX(-100%);
}
&.center {
left: var(--trigger-center);
transform: translateX(-50%);
}
}
`,
})
export class BTPopover extends HTMLDivElement {
connectedCallback() {
this.addEventListener('beforetoggle', () => {
this.style.opacity = '0';
});
this.addEventListener('toggle', () => {
const trigger = document.querySelector(
`[popovertarget="${this.id}"]`,
) as HTMLElement;
this.style.setProperty(
'--trigger-bottom',
`${trigger.getBoundingClientRect().bottom}px`,
);
this.style.setProperty(
'--trigger-right',
`${trigger.getBoundingClientRect().right}px`,
);
this.style.setProperty(
'--trigger-left',
`${trigger.getBoundingClientRect().left}px`,
);
this.style.setProperty(
'--trigger-center',
`${(trigger.getBoundingClientRect().right - trigger.getBoundingClientRect().left) / 2}px`,
);
const overflows = ['left', 'right', 'center'].map((position) => {
this.classList.add(position);
const measure = {
position,
overflow: Math.max(
-Math.min(this.getBoundingClientRect().left, 0),
-Math.min(
document.body.clientWidth - this.getBoundingClientRect().right,
0,
),
),
};
this.classList.remove('left', 'right', 'center');
return measure;
});
overflows.sort((a, b) => a.overflow - b.overflow);
this.classList.add(overflows[0].position);
this.style.opacity = '1';
});
}
}
Loading

0 comments on commit a1913f1

Please sign in to comment.