Skip to content

Commit ae602a8

Browse files
committed
✨ Added Context Menu component
1 parent 6069d6e commit ae602a8

File tree

3 files changed

+168
-0
lines changed

3 files changed

+168
-0
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
context-menu {
2+
display: inline-block;
3+
position: fixed;
4+
top: 0;
5+
left: 0;
6+
background-color: var(--grey-800);
7+
box-shadow: var(--shadow-grey-sm);
8+
border: 1px solid var(--grey-600);
9+
padding: 0.5rem 0;
10+
min-width: 200px;
11+
12+
button {
13+
white-space: nowrap;
14+
display: flex;
15+
justify-content: space-between;
16+
align-items: center;
17+
flex-flow: row nowrap;
18+
padding: 0 1rem;
19+
width: 100%;
20+
height: 24px;
21+
22+
&:hover,
23+
&:focus-visible {
24+
background-color: var(--primary-500);
25+
}
26+
27+
span {
28+
display: inline-block;
29+
font-size: var(--font-xs);
30+
color: var(--white);
31+
}
32+
}
33+
34+
hr {
35+
display: block;
36+
width: 100%;
37+
height: 1px;
38+
background-color: var(--grey-600);
39+
margin: 0.5rem 0;
40+
}
41+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { html, render, TemplateResult } from "lit-html";
2+
import SuperComponent from "@codewithkyle/supercomponent";
3+
import env from "~brixi/controllers/env";
4+
import pos from "~brixi/controllers/pos";
5+
6+
export interface ContextMenuItem {
7+
label: string;
8+
hotkey?: string;
9+
callback: Function;
10+
}
11+
export interface IContextMenu {
12+
items: ContextMenuItem[];
13+
x: number;
14+
y: number;
15+
}
16+
export interface ContextMenuSettings {
17+
items: ContextMenuItem[];
18+
x: number;
19+
y: number;
20+
}
21+
export default class ContextMenu extends SuperComponent<IContextMenu> {
22+
constructor(settings: ContextMenuSettings) {
23+
super();
24+
this.model = {
25+
items: [],
26+
x: 0,
27+
y: 0,
28+
};
29+
env.css(["context-menu"]).then(() => {
30+
this.set(settings);
31+
});
32+
}
33+
34+
override connected() {
35+
document.addEventListener(
36+
"click",
37+
() => {
38+
this.remove();
39+
},
40+
{ passive: true, capture: true }
41+
);
42+
window.addEventListener(
43+
"resize",
44+
() => {
45+
this.remove();
46+
},
47+
{ passive: true, capture: true }
48+
);
49+
window.addEventListener(
50+
"scroll",
51+
() => {
52+
this.remove();
53+
},
54+
{ passive: true, capture: true }
55+
);
56+
this.addEventListener("click", (e: Event) => {
57+
e.stopImmediatePropagation();
58+
});
59+
}
60+
61+
private handleItemClick: EventListener = (e: Event) => {
62+
const target = e.currentTarget as HTMLElement;
63+
const index = parseInt(target.dataset.index);
64+
this.model.items?.[index]?.callback();
65+
};
66+
67+
private renderItem(item: ContextMenuItem, index: number): TemplateResult {
68+
if (item === null) {
69+
return html`<hr />`;
70+
}
71+
return html`
72+
<button sfx="button" type="button" @click=${this.handleItemClick} data-index="${index}">
73+
<span>${item.label}</span>
74+
${item.hotkey ? html`<span class="font-grey-400">${item.hotkey}</span>` : ""}
75+
</button>
76+
`;
77+
}
78+
79+
override render() {
80+
pos.positionElement(this, this.model.x, this.model.y);
81+
const view = html` ${this.model.items?.map((item, index) => this.renderItem(item, index))} `;
82+
render(view, this);
83+
}
84+
}
85+
env.bind("context-menu", ContextMenu);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<script type="module">
2+
import Component from "/js/context-menu.js";
3+
const example = new Component({
4+
items: [
5+
{
6+
label: "Back",
7+
callback: () => {
8+
console.log("Back");
9+
},
10+
},
11+
{
12+
label: "Forward",
13+
callback: () => {
14+
console.log("Forward");
15+
},
16+
},
17+
{
18+
label: "Reload",
19+
hotkey: "Ctrl+R",
20+
callback: () => {
21+
location.reload();
22+
},
23+
},
24+
null,
25+
{
26+
label: "Action 1",
27+
callback: () => {
28+
console.log("Action 1");
29+
},
30+
},
31+
{
32+
label: "Action 2",
33+
callback: () => {
34+
console.log("Action 2");
35+
},
36+
},
37+
],
38+
x: 24,
39+
y: 24,
40+
});
41+
document.body.appendChild(example);
42+
</script>

0 commit comments

Comments
 (0)