Skip to content

Commit

Permalink
add icon to dropdown
Browse files Browse the repository at this point in the history
  • Loading branch information
mucsi96 committed Aug 25, 2024
1 parent bd264dd commit 8e43b9f
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 13 deletions.
6 changes: 6 additions & 0 deletions src/Button/Button.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,10 @@ button[bt] {
--button-color: hsl(42, 93%, 46%);
--button-hover-color: hsl(37, 97%, 39%);
}

&[bt-dropdown] > svg {
width: 0.75em;
height: 0.75em;
margin-inline-start: 0.4em;
}
}
20 changes: 20 additions & 0 deletions src/Button/Button.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,23 @@ export const Red: Story = {
export const Yellow: Story = {
render: () => html`<button bt bt-yellow>Button text</button>`,
};

export const Dropdown: Story = {
render: () =>
html`<button bt bt-dropdown popovertarget="dropdown-menu-popover">
Open
<svg></svg>
</button>
<div popover bt id="dropdown-menu-popover">
<ul bt-dropdown-menu>
<li bt-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 bt-separated><a href="#">Menu item 3</a></li>
<li><button type="button">Sign out</button></li>
</ul>
</div>`,
};
9 changes: 8 additions & 1 deletion src/Button/Button.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { injectStyles } from '../utils';
import { injectStyles, onElementConnected } from '../utils';
import styles from './Button.css?raw&inline';
import dropdownSvg from './dropdown.svg?raw&inline';

injectStyles(styles);

onElementConnected('button[bt-dropdown] > svg', (svg) => {
if (!svg.hasAttribute('viewBox')) {
svg.outerHTML = dropdownSvg;
}
});
5 changes: 5 additions & 0 deletions src/Button/dropdown.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions src/Docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,49 +39,70 @@ body {

# Avatar

Selector: `button[bt-avatar]`

<Canvas of={AvatarStories.Default} />

# Badge

Selector: `span[bt-badge]`

<Canvas of={BadgeStories.Normal} />
<Canvas of={BadgeStories.Large} />

# Button

Selector: `button[bt]`

<Canvas of={ButtonStories.Default} />
<Canvas of={ButtonStories.Green} />
<Canvas of={ButtonStories.Red} />
<Canvas of={ButtonStories.Yellow} />
<Canvas of={ButtonStories.Dropdown} />

# DropdownMenu

Selector: `ul[bt-dropdown-menu]`

<Canvas of={DropdownMenuStories.Default} />

# Header

Selector: `header[bt]`

<Canvas of={HeaderStories.Default} />

# Heading

Selector: `h1[bt], h2[bt], h3[bt], h4[bt], h5[bt], h6[bt]`

<Canvas of={HeadingStories.Level1} />
<Canvas of={HeadingStories.Level2} />
<Canvas of={HeadingStories.Level3} />
<Canvas of={HeadingStories.Level4} />

# Input label

Selector: `label[bt]`

<Canvas of={InputLabelStories.Default} />

# Loader

Selector: `[role="progressbar"][bt]`

<Canvas of={LoaderStories.Default} />

# Main

Selector: `main[bt]`

<Canvas of={MainStories.Default} />

# Notifications

Selector: `section[bt-notifications]`

<Canvas
of={NotificationStories.Default}
source={{
Expand All @@ -102,15 +123,21 @@ document.dispatchEvent(new ErrorNotificationEvent('Failure'));

# Popover

Selector: `[popover][bt]`

<Canvas of={PopoverStories.Default} />

# Table

Selector: `table[bt]`

<Canvas of={TableStories.Default} />
<Canvas of={TableStories.WithSelection} />

# Text

Selector: `[bt-text]`

<Canvas of={TextStories.Default} />
<Canvas of={TextStories.Green} />
<Canvas of={TextStories.Red} />
Expand Down
4 changes: 2 additions & 2 deletions src/DropdownMenu/DropdownMenu.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { html } from 'lit';
const meta: Meta = {
title: 'BTDropdownMenu',
render: () => {
return html`<button bt type="button" popovertarget="dropdown-menu-popover">
Open
return html`<button bt bt-dropdown type="button" popovertarget="dropdown-menu-popover">
Open <svg></svg>
</button>
<div popover bt id="dropdown-menu-popover">
<ul bt-dropdown-menu>
Expand Down
47 changes: 37 additions & 10 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,44 @@ export function injectStyles(styles: string) {
document.head.append(style);
}

const listeners: Record<string, (element: HTMLElement) => void> = {};
const listeners: Record<string, (element: HTMLElement | SVGElement) => void> =
{};

const observer = new MutationObserver((mutations) => {
const connected = new Set<HTMLElement>();
const connected = new Set<HTMLElement | SVGElement>();
const selector = Object.keys(listeners).join(',');

for (const { addedNodes, removedNodes } of mutations) {
for (const node of addedNodes) {
if (!(node instanceof HTMLElement)) continue;
if (!(node instanceof HTMLElement) && !(node instanceof SVGElement))
continue;

if (node.matches(selector)) {
connected.add(node);
}

for (const child of node.querySelectorAll(selector)) {
if (!(child instanceof HTMLElement)) continue;
for (const child of node.querySelectorAll(
getChildSelector(node, selector),
)) {
if (!(child instanceof HTMLElement) && !(child instanceof SVGElement))
continue;

connected.add(child);
}
}
for (const node of removedNodes) {
if (!(node instanceof HTMLElement)) continue;
if (!(node instanceof HTMLElement) && !(node instanceof SVGElement))
continue;

if (node.matches(selector)) {
connected.delete(node);
}

for (const child of node.querySelectorAll(selector)) {
if (!(child instanceof HTMLElement)) continue;
for (const child of node.querySelectorAll(
getChildSelector(node, selector),
)) {
if (!(child instanceof HTMLElement) && !(child instanceof SVGElement))
continue;

connected.delete(child);
}
Expand All @@ -54,10 +63,28 @@ observer.observe(document, {

export function onElementConnected(
selector: string,
callback: (element: HTMLElement) => void,
callback: (element: HTMLElement | SVGElement) => void,
) {
document
.querySelectorAll(selector)
.forEach((element) => element instanceof HTMLElement && callback(element));
.forEach(
(element) =>
(element instanceof HTMLElement || element instanceof SVGElement) &&
callback(element),
);
listeners[selector] = callback;
}

function getChildSelector(node: HTMLElement | SVGElement, selector: string) {
const selectorSegements = selector.split(/[ >]/);

if (selectorSegements.length === 1) {
return selector;
}

const childSelector = node.matches(selectorSegements[0])
? selector.slice(selectorSegements[0].length).trim().replace(/^>/, '')
: selector;

return childSelector;
}

0 comments on commit 8e43b9f

Please sign in to comment.