Skip to content

Commit a163621

Browse files
committed
♻ Refactored Split Button to use new Overflow Menu
1 parent ae602a8 commit a163621

File tree

5 files changed

+35
-162
lines changed

5 files changed

+35
-162
lines changed

src/framework/components/buttons/overflow-button/overflow-button.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,12 @@ export default class OverflowButton extends SuperComponent<IOverflowButton> {
7676

7777
private handleClick: EventListener = (e: Event) => {
7878
e.stopImmediatePropagation();
79-
const target = e.currentTarget as HTMLElement;
80-
const container = new OverflowMenu(this.uid, this.model.items);
79+
const container = new OverflowMenu({
80+
uid: this.uid,
81+
items: this.model.items,
82+
target: this,
83+
});
8184
document.body.appendChild(container);
82-
pos.positionElementToElement(container, target);
8385
};
8486

8587
override render() {

src/framework/components/buttons/split-button/split-button.scss

Lines changed: 0 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -94,113 +94,4 @@ split-button {
9494
}
9595
}
9696
}
97-
98-
button-menu {
99-
opacity: 0;
100-
border-radius: 0.25rem;
101-
background-color: var(--white);
102-
border: 1px solid var(--grey-300);
103-
box-shadow: var(--shadow-grey-sm);
104-
padding: 0.25rem 0;
105-
display: inline-block;
106-
position: absolute;
107-
top: calc(100% + 0.25rem);
108-
right: 0;
109-
z-index: 1000;
110-
pointer-events: none;
111-
visibility: hidden;
112-
113-
&:focus-within {
114-
pointer-events: all;
115-
opacity: 1;
116-
visibility: visible;
117-
}
118-
119-
button {
120-
width: 100%;
121-
display: flex;
122-
align-items: center;
123-
flex-flow: row nowrap;
124-
min-height: 36px;
125-
padding: 0 1rem;
126-
color: var(--grey-700);
127-
position: relative;
128-
font-size: var(--font-sm);
129-
font-weight: var(--font-medium);
130-
line-height: 1;
131-
white-space: nowrap;
132-
outline-offset: 0;
133-
134-
&:focus-visible {
135-
outline: var(--focus-ring);
136-
outline-offset: var(--focus-ring-offset);
137-
transition: outline-offset 80ms var(--ease-in-out);
138-
}
139-
140-
&:hover,
141-
&:focus-visible {
142-
&::before {
143-
opacity: 0.05;
144-
}
145-
}
146-
147-
&:active {
148-
outline-offset: 0;
149-
150-
&::before {
151-
opacity: 0.1;
152-
}
153-
}
154-
155-
&::before {
156-
content: "";
157-
display: inline-block;
158-
position: absolute;
159-
top: 0;
160-
left: 0;
161-
width: 100%;
162-
height: 100%;
163-
opacity: 0;
164-
background-color: var(--grey-500);
165-
transition: all 80ms var(--ease-in-out);
166-
}
167-
168-
&.danger {
169-
color: var(--danger-700);
170-
171-
&::before {
172-
background-color: var(--danger-500);
173-
}
174-
175-
i {
176-
svg {
177-
color: var(--danger-700);
178-
}
179-
}
180-
}
181-
182-
i {
183-
display: inline-flex;
184-
justify-content: center;
185-
align-items: center;
186-
margin-right: 0.5rem;
187-
margin-left: -0.375rem;
188-
width: 24px;
189-
height: 24px;
190-
191-
svg {
192-
width: 18px;
193-
height: 18px;
194-
color: var(--grey-600);
195-
}
196-
}
197-
}
198-
199-
hr {
200-
display: block;
201-
width: 100%;
202-
margin: 0.25rem 0;
203-
border-bottom: 1px solid var(--grey-300);
204-
}
205-
}
20697
}

src/framework/components/buttons/split-button/split-button.ts

Lines changed: 14 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import SuperComponent from "@codewithkyle/supercomponent";
33
import env from "~brixi/controllers/env";
44
import { noop, parseDataset } from "~brixi/utils/general";
55
import { unsafeHTML } from "lit-html/directives/unsafe-html";
6+
import OverflowMenu, { OverflowItem } from "~brixi/components/overflow-menu/overflow-menu";
7+
import { UUID } from "@codewithkyle/uuid";
68

79
type ButtonType = "submit" | "button" | "reset";
810

@@ -15,13 +17,7 @@ export interface ISplitButton {
1517
type: ButtonType;
1618
label: string;
1719
icon?: string | HTMLElement;
18-
buttons: Array<{
19-
label: string;
20-
type?: ButtonType;
21-
icon?: string | HTMLElement;
22-
callback: Function;
23-
danger?: boolean;
24-
}>;
20+
buttons: OverflowItem[];
2521
callback: Function;
2622
}
2723
export interface SplitButtonSettings {
@@ -33,18 +29,15 @@ export interface SplitButtonSettings {
3329
type: ButtonType;
3430
label: string;
3531
icon?: string | HTMLElement;
36-
buttons: Array<{
37-
label: string;
38-
type?: ButtonType;
39-
icon?: string | HTMLElement;
40-
callback: Function;
41-
danger?: boolean;
42-
}>;
32+
buttons: OverflowItem[];
4333
callback: Function;
4434
}
4535
export default class SplitButton extends SuperComponent<ISplitButton> {
36+
private uid: string;
37+
4638
constructor(settings: SplitButtonSettings) {
4739
super();
40+
this.uid = UUID();
4841
this.model = {
4942
css: "",
5043
class: "",
@@ -73,22 +66,14 @@ export default class SplitButton extends SuperComponent<ISplitButton> {
7366
this.model.callback();
7467
};
7568

76-
private handleSecondaryClick = (e: Event) => {
77-
const target = e.currentTarget as HTMLElement;
78-
const index = parseInt(target.dataset.index);
79-
this.model.buttons[index].callback();
80-
};
81-
8269
private openMenu = () => {
83-
const buttonMenu: HTMLElement = this.querySelector("button-menu");
84-
if (buttonMenu) {
85-
buttonMenu.style.visibility = "visible";
86-
const firstMenuBttn: HTMLElement = buttonMenu.querySelector("button-menu button");
87-
console.log(firstMenuBttn);
88-
if (firstMenuBttn) {
89-
firstMenuBttn.focus();
90-
}
91-
}
70+
const menu = new OverflowMenu({
71+
target: this,
72+
uid: this.uid,
73+
items: this.model.buttons,
74+
offset: 4,
75+
});
76+
document.body.appendChild(menu);
9277
};
9378

9479
private renderIcon(icon: string | HTMLElement): string | TemplateResult {
@@ -127,19 +112,6 @@ export default class SplitButton extends SuperComponent<ISplitButton> {
127112
<polyline points="6 9 12 15 18 9"></polyline>
128113
</svg>
129114
</button>
130-
<button-menu>
131-
${this.model.buttons.map((button, i) => {
132-
if (button === null) {
133-
return html`<hr />`;
134-
} else {
135-
return html`
136-
<button class="${button?.danger ? "danger" : ""}" type="${button?.type ?? "button"}" @click=${this.handleSecondaryClick} data-index="${i}">
137-
${this.renderIcon(button?.icon ?? "")} ${this.renderLabel(button.label)}
138-
</button>
139-
`;
140-
}
141-
})}
142-
</button-menu>
143115
`;
144116
} else {
145117
out = "";

src/framework/components/overflow-menu/overflow-menu.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import SuperComponent from "@codewithkyle/supercomponent";
22
import { html, render } from "lit-html";
33
import { unsafeHTML } from "lit-html/directives/unsafe-html";
44
import env from "~brixi/controllers/env";
5+
import pos from "~brixi/controllers/pos";
56

67
export interface OverflowItem {
78
label: string;
@@ -12,16 +13,20 @@ export interface OverflowItem {
1213
export interface IOverflowMenu {
1314
items: Array<OverflowItem>;
1415
uid: string;
16+
offset?: number;
17+
target: HTMLElement;
1518
}
1619
export default class OverflowMenu extends SuperComponent<IOverflowMenu> {
17-
constructor(uid: string, items: Array<OverflowItem>) {
20+
constructor(settings: IOverflowMenu) {
1821
super();
1922
this.model = {
20-
items: items,
21-
uid: uid,
23+
items: [],
24+
uid: "",
25+
offset: 0,
26+
target: null,
2227
};
2328
env.css("overflow-menu").then(() => {
24-
this.render();
29+
this.set(settings);
2530
});
2631
}
2732

@@ -78,6 +83,7 @@ export default class OverflowMenu extends SuperComponent<IOverflowMenu> {
7883
})}
7984
`;
8085
render(view, this);
86+
pos.positionElementToElement(this, this.model.target, this.model.offset);
8187
}
8288
}
8389
env.bind("overflow-menu", OverflowMenu);

src/framework/controllers/pos.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,18 @@ class Positions {
4949
el.style.transform = `translate(${x}px, ${y}px)`;
5050
}
5151

52-
public positionElementToElement(el: HTMLElement, target: HTMLElement): void {
52+
public positionElementToElement(el: HTMLElement, target: HTMLElement, offset: number = 0): void {
5353
const elBounds = el.getBoundingClientRect();
5454
const targetBounds = target.getBoundingClientRect();
55-
let top = targetBounds.top + targetBounds.height;
55+
let top = targetBounds.top + targetBounds.height + offset;
5656
if (top + elBounds.height >= this.window.innerHeight) {
57-
top = targetBounds.top - elBounds.height;
57+
top = targetBounds.top - elBounds.height - offset;
5858
}
59-
let left = targetBounds.left;
59+
let left = targetBounds.right - elBounds.width;
6060
if (left + elBounds.width >= this.window.innerWidth) {
6161
left = this.window.innerWidth - elBounds.width;
62+
} else if (left < 0) {
63+
left = 0;
6264
}
6365
el.style.transform = `translate(${left}px, ${top}px)`;
6466
}

0 commit comments

Comments
 (0)