Get menu alignment to somewhat work

This commit is contained in:
Tamius Han 2025-12-29 11:32:49 +01:00
parent af08c5094d
commit 14b3571af3
4 changed files with 166 additions and 25 deletions

View File

@ -5,19 +5,20 @@ export interface MenuItemConfig {
customHTML?: HTMLElement; customHTML?: HTMLElement;
} }
export type MenuAnchor = export enum MenuPosition {
| "LeftCenter" TopLeft = 'top-left',
| "RightCenter" Left = 'left',
| "TopCenter" BottomLeft = 'bottom-left',
| "BottomCenter" Top = 'top',
| "TopLeft" Bottom = 'bottom',
| "TopRight" TopRight = 'top-right',
| "BottomLeft" Right = 'right',
| "BottomRight"; BottomRight = 'bottom-right',
}
export interface MenuConfig { export interface MenuConfig {
isGlobal?: boolean; isGlobal?: boolean;
anchor: MenuAnchor; menuPosition: MenuPosition;
activationRadius: number; activationRadius?: number;
items: MenuItemConfig[]; items: MenuItemConfig[];
} }

View File

@ -1,4 +1,4 @@
import { MenuConfig, MenuItemConfig } from '@src/common/interfaces/ClientUiMenu.interface'; import { MenuPosition, MenuConfig, MenuItemConfig } from '@src/common/interfaces/ClientUiMenu.interface';
import extensionCss from '@src/main.css?inline'; import extensionCss from '@src/main.css?inline';
export class ClientMenu { export class ClientMenu {
@ -8,9 +8,12 @@ export class ClientMenu {
private root!: HTMLDivElement; private root!: HTMLDivElement;
private visible = false; private visible = false;
private menuPositionClasses: string[] = [];
constructor(private config: MenuConfig) {} constructor(private config: MenuConfig) {}
mount(anchorElement: HTMLElement) { mount(anchorElement: HTMLElement) {
this.buildMenuPositionClassList();
this.createHost(); this.createHost();
this.createShadow(); this.createShadow();
this.createMenu(); this.createMenu();
@ -76,7 +79,6 @@ export class ClientMenu {
this.shadow.appendChild(style); this.shadow.appendChild(style);
} }
private createHost() { private createHost() {
this.host = document.createElement("div"); this.host = document.createElement("div");
this.host.classList.add('uw-ultrawidify-container-root'); this.host.classList.add('uw-ultrawidify-container-root');
@ -90,7 +92,6 @@ export class ClientMenu {
height: "100%", height: "100%",
zIndex: this.config.isGlobal ? "2147483647" : "2147483640", zIndex: this.config.isGlobal ? "2147483647" : "2147483640",
// pointerEvents: "none", // pointerEvents: "none",
backgroundColor: "#00000099",
}); });
console.log('UI host created:', this.host); console.log('UI host created:', this.host);
@ -101,30 +102,74 @@ export class ClientMenu {
this.injectStyles(); this.injectStyles();
} }
/**
*
* @returns
*/
private buildMenuPositionClassList() {
let classList;
switch (this.config.menuPosition) {
case MenuPosition.TopLeft:
classList = ['uw-menu-left','uw-menu-top'];
break;
case MenuPosition.Left:
classList = ['uw-menu-left','uw-menu-ycenter'];
break;
case MenuPosition.BottomLeft:
classList = ['uw-menu-left','uw-menu-bottom'];
break;
case MenuPosition.Top:
classList = ['uw-menu-center','uw-menu-top'];
break;
case MenuPosition.Bottom:
classList = ['uw-menu-center', 'uw-menu-bottom'];
break;
case MenuPosition.TopRight:
classList = ['uw-menu-right', 'uw-menu-top'];
break;
case MenuPosition.Right:
classList = ['uw-menu-right', 'uw-menu-ycenter'];
break;
case MenuPosition.BottomRight:
classList = ['uw-menu-right', 'uw-menu-bottom'];
break;
default: // left-center is our default position
classList = ['uw-menu-left', 'uw-menu-ycenter'];
break;
}
this.menuPositionClasses = classList;
}
private createMenu() { private createMenu() {
this.root = document.createElement("div"); this.root = document.createElement("div");
this.root.className = "uw-menu-root font-mono"; this.root.className = "uw-menu-root font-mono";
const trigger = document.createElement("div"); const trigger = document.createElement("div");
trigger.className = "uw-menu-trigger"; trigger.classList = "uw-menu-trigger uw-trigger";
trigger.textContent = "☰"; trigger.textContent = "Ultrawidify";
const submenu = this.buildSubmenu(this.config.items); const submenu = this.buildSubmenu(this.config.items);
trigger.addEventListener("mouseenter", () => this.show()); trigger.addEventListener("mouseenter", () => this.show());
this.root.addEventListener("mouseleave", () => this.hide()); this.root.addEventListener("mouseleave", () => this.hide());
this.root.append(trigger, submenu); console.log('menu position classes:', this.menuPositionClasses)
this.root.classList.add(...this.menuPositionClasses);
trigger.appendChild(submenu);
this.root.append(trigger);
this.shadow.appendChild(this.root); this.shadow.appendChild(this.root);
} }
private buildSubmenu(items: MenuItemConfig[]): HTMLDivElement { private buildSubmenu(items: MenuItemConfig[]): HTMLDivElement {
const menu = document.createElement("div"); const menu = document.createElement("div");
menu.className = "uw-submenu"; menu.classList = "uw-submenu";
menu.classList.add(...this.menuPositionClasses);
for (const item of items) { for (const item of items) {
const el = document.createElement("div"); const el = document.createElement("div");
el.className = "uw-menu-item"; el.className = `uw-menu-item uw-trigger`;
if (item.customHTML) { if (item.customHTML) {
el.appendChild(item.customHTML); el.appendChild(item.customHTML);
@ -151,8 +196,11 @@ export class ClientMenu {
} }
private position(anchorEl: HTMLElement) { private position(anchorEl: HTMLElement) {
const rect = anchorEl.getBoundingClientRect(); let appendClassList: string[] = [];
const r = this.root.style;
anchorEl.classList.add(...appendClassList);
// switch (this.config.anchor) { // switch (this.config.anchor) {
// case "LeftCenter": // case "LeftCenter":
@ -188,14 +236,15 @@ export class ClientMenu {
private show() { private show() {
if (!this.visible) { if (!this.visible) {
this.visible = true; this.visible = true;
this.root.classList.add("visible"); this.root.classList.add("uw-visible");
this.root.classList.remove("uw-hidden");
} }
} }
private hide() { private hide() {
if (this.visible) { if (this.visible) {
this.visible = false; this.visible = false;
this.root.classList.remove("visible"); this.root.classList.remove("uw-visible");
} }
} }
} }

View File

@ -3,6 +3,7 @@ import { ClientMenu } from './ClientMenu';
import EventBus from '../EventBus'; import EventBus from '../EventBus';
import PlayerData from '../video-data/PlayerData'; import PlayerData from '../video-data/PlayerData';
import { SiteSettings } from '../settings/SiteSettings'; import { SiteSettings } from '../settings/SiteSettings';
import { MenuPosition as MenuPosition } from '../../../common/interfaces/ClientUiMenu.interface';
if (process.env.CHANNEL !== 'stable'){ if (process.env.CHANNEL !== 'stable'){
console.info("Loading: UI"); console.info("Loading: UI");
@ -118,7 +119,14 @@ class UI {
const uwid = `uw-ultrawidify-${this.interfaceId}-root-${random}` const uwid = `uw-ultrawidify-${this.interfaceId}-root-${random}`
if (this.uiConfig.parentElement) { if (this.uiConfig.parentElement) {
const uwMenu = new ClientMenu({isGlobal: this.isGlobal, anchor: "LeftCenter", items: [{label: 'test'}]}); const uwMenu = new ClientMenu({
isGlobal: this.isGlobal,
menuPosition: MenuPosition.Left,
items: [
{label: 'test'},
{label: 'test nested', subitems: [{label: 'sub test'}, {label: 'sub test 2'}]}
]
});
uwMenu.mount(this.uiConfig.parentElement); uwMenu.mount(this.uiConfig.parentElement);
} }
return; return;

View File

@ -2,9 +2,92 @@
@import "tailwindcss/utilities"; @import "tailwindcss/utilities";
.uw-menu-root { .uw-menu-root {
@apply h-full w-full font-mono text-stone-200 text-[16px]; @apply h-full w-full font-mono text-stone-200 text-[16px] flex flex-row;
&.uw-menu-left {
@apply justify-start;
}
&.uw-menu-right {
@apply justify-end;
}
&.uw-menu-left, &.uw-menu-right {
&.uw-menu-top {
@apply items-start;
}
&.uw-menu-ycenter {
@apply items-center;
}
&.uw-menu-bottom {
@apply items-end;
}
}
&.uw-menu-center {
@apply flex-col items-center;
&.uw-menu-top {
@apply justify-start;
}
&.uw-menu-bottom {
@apply justify-end;
}
}
* { * {
@apply font-mono text-stone-200 text-[1em]; @apply font-mono text-stone-200 text-[1em];
} }
} }
.uw-menu-trigger {
}
.uw-submenu {
@apply absolute flex flex-col gap-0;
&.uw-menu-left {
@apply left-full;
}
&.uw-menu-right {
@apply right-full;
}
&.uw-menu-left, &.uw-menu-right {
&.uw-menu-top {
@apply top-1/2 -translate-y-1/2;
}
&.uw-menu-ycenter {
@apply top-0;
}
&.uw-menu-bottom {
@apply bottom-0;
}
}
&.uw-menu-center {
@apply left-1/2 -translate-x-1/2 flex-row;
&.uw-menu-top {
@apply bottom-0;
}
&.uw-menu-bottom {
@apply top-0;
}
}
* {
@apply font-mono text-stone-200 text-[1em];
}
}
.uw-trigger {
@apply p-4 relative block
bg-stone-950/75 backdrop-blur-[1em] backdrop-brightness-75 backdrop-saturate-50
text-nowrap
;
&:hover {
@apply text-primary-200 bg-stone-800/75;
}
}