nplayer
Version:
powerful danmaku video player
101 lines (82 loc) • 2.79 kB
text/typescript
import { Player } from 'src/ts/player';
import {
$, addClass, show, hide, addDisposableListener, Rect, Component,
} from 'src/ts/utils';
export interface ContextMenuItem {
id?: string;
html?: string;
disabled?: boolean;
invisible?: boolean;
checked?: boolean;
init?: (player: Player, item: ContextMenuItem) => void;
show?: (player: Player, item: ContextMenuItem) => void;
click?: (player: Player, item: ContextMenuItem) => void;
}
export class ContextMenu extends Component {
private rect: Rect;
private showed = false;
constructor(
container: HTMLElement,
private player: Player,
private items: ContextMenuItem[],
) {
super(container, '.contextmenu');
hide(this.el);
this.rect = new Rect(this.el, player);
this.items.forEach((item) => item.init && item.init(player, item));
addDisposableListener(this, player.el, 'contextmenu', (ev: MouseEvent) => {
this.hide();
if (!player.opts.contextMenuToggle || !this.showed) {
ev.preventDefault();
ev.stopPropagation();
if (this.renderItems()) {
show(this.el);
this.rect.update();
this.player.rect.update();
const { width, height } = this.rect;
const { x, y } = this.player.rect;
const { innerWidth, innerHeight } = window;
const { clientX, clientY } = ev;
let left = clientX - x;
let top = clientY - y;
if (clientX + width > innerWidth) left = innerWidth - width;
if (clientY + height > innerHeight) top = innerHeight - height;
this.applyStyle({ left: `${left}px`, top: `${top}px` });
}
}
this.showed = !this.showed;
});
addDisposableListener(this, document, 'click', () => {
this.showed = false;
this.hide();
});
}
private getDomNodes(): HTMLElement[] {
return this.items.filter((x) => x && !x.invisible).map((item) => {
const el = $('.contextmenu_item');
if (item.show) item.show(this.player, item);
if (item.html) el.innerHTML = item.html;
if (item.disabled) addClass(el, 'contextmenu_item-disabled');
if (item.checked) addClass(el, 'contextmenu_item-checked');
if (item.click) {
el.addEventListener('click', () => (item as any).click(this.player, item), false);
}
return el;
});
}
private renderItems(): boolean {
const items = this.getDomNodes();
if (!items.length) return false;
this.el.textContent = '';
const frag = document.createDocumentFragment();
items.forEach((item) => frag.appendChild(item));
this.el.appendChild(frag);
return true;
}
get isActive(): boolean {
return this.el.style.display !== 'none';
}
hide = () => {
hide(this.el);
}
}