mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
172 lines
4.8 KiB
JavaScript
172 lines
4.8 KiB
JavaScript
import createPopoverDefault from "./overlay/popoverEngine.js";
|
|
import { createVideoSettingsMenuPanel as createVideoSettingsMenuPanelDefault } from "./components/videoMenuRenderers.js";
|
|
import { userLogger } from "../utils/logger.js";
|
|
import { normalizeDesignSystemContext } from "../designSystem.js";
|
|
|
|
export default class VideoSettingsMenuController {
|
|
constructor(options = {}) {
|
|
const {
|
|
designSystem = null,
|
|
isDevMode = false,
|
|
createPopover = createPopoverDefault,
|
|
createVideoSettingsMenuPanel = createVideoSettingsMenuPanelDefault,
|
|
} = options;
|
|
|
|
this.createPopover = createPopover;
|
|
this.createVideoSettingsMenuPanel = createVideoSettingsMenuPanel;
|
|
this.designSystem = normalizeDesignSystemContext(designSystem);
|
|
this.isDevMode = Boolean(isDevMode);
|
|
this.popovers = new Map();
|
|
}
|
|
|
|
ensurePopover(detail = {}) {
|
|
const trigger = detail.trigger || null;
|
|
if (!trigger) {
|
|
return null;
|
|
}
|
|
|
|
let entry = this.popovers.get(trigger);
|
|
if (!entry) {
|
|
entry = {
|
|
trigger,
|
|
context: {
|
|
card: detail.card || null,
|
|
video: detail.video || null,
|
|
index: Number.isFinite(detail.index) ? Math.floor(detail.index) : 0,
|
|
capabilities: detail.capabilities || {},
|
|
restoreFocusOnClose: detail.restoreFocus !== false,
|
|
},
|
|
popover: null,
|
|
};
|
|
|
|
const render = ({ document: documentRef, close }) => {
|
|
const panel = this.createVideoSettingsMenuPanel({
|
|
document: documentRef,
|
|
video: entry.context.video,
|
|
index: entry.context.index,
|
|
capabilities: entry.context.capabilities,
|
|
designSystem: this.designSystem,
|
|
});
|
|
|
|
if (!panel) {
|
|
return null;
|
|
}
|
|
|
|
const buttons = panel.querySelectorAll("button[data-action]");
|
|
buttons.forEach((button) => {
|
|
button.addEventListener("click", (event) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
const action = button.dataset.action || "";
|
|
const handled = entry.context.card?.handleSettingsMenuAction?.(
|
|
action,
|
|
{ event },
|
|
);
|
|
|
|
if (!handled && this.isDevMode) {
|
|
userLogger.warn(`[SettingsMenu] Unhandled action: ${action}`);
|
|
}
|
|
|
|
close();
|
|
});
|
|
});
|
|
|
|
return panel;
|
|
};
|
|
|
|
const documentRef =
|
|
trigger.ownerDocument ||
|
|
(typeof document !== "undefined" ? document : null);
|
|
|
|
const popover = this.createPopover(trigger, render, {
|
|
document: documentRef,
|
|
placement: "bottom-end",
|
|
});
|
|
|
|
const originalDestroy = popover.destroy?.bind(popover);
|
|
if (typeof originalDestroy === "function") {
|
|
popover.destroy = (...args) => {
|
|
originalDestroy(...args);
|
|
if (this.popovers.get(trigger) === entry) {
|
|
this.popovers.delete(trigger);
|
|
}
|
|
};
|
|
}
|
|
|
|
entry.popover = popover;
|
|
this.popovers.set(trigger, entry);
|
|
}
|
|
|
|
entry.context = {
|
|
...entry.context,
|
|
card: detail.card || entry.context.card,
|
|
video: detail.video || entry.context.video,
|
|
index: Number.isFinite(detail.index)
|
|
? Math.floor(detail.index)
|
|
: entry.context.index,
|
|
capabilities: detail.capabilities || entry.context.capabilities,
|
|
restoreFocusOnClose: detail.restoreFocus !== false,
|
|
};
|
|
|
|
return entry;
|
|
}
|
|
|
|
requestMenu(detail = {}) {
|
|
const entry = this.ensurePopover(detail);
|
|
if (!entry?.popover) {
|
|
return;
|
|
}
|
|
|
|
if (typeof entry.popover.isOpen === "function" && entry.popover.isOpen()) {
|
|
entry.popover.close({
|
|
restoreFocus: entry.context.restoreFocusOnClose !== false,
|
|
});
|
|
return;
|
|
}
|
|
|
|
entry.popover
|
|
.open()
|
|
.catch((error) =>
|
|
userLogger.error("[SettingsMenu] Failed to open popover:", error),
|
|
);
|
|
}
|
|
|
|
closeMenu(detail = {}) {
|
|
const trigger = detail.trigger || null;
|
|
const restoreFocus = detail.restoreFocus !== false;
|
|
|
|
if (trigger) {
|
|
const entry = this.popovers.get(trigger);
|
|
if (entry?.popover && typeof entry.popover.close === "function") {
|
|
return entry.popover.close({ restoreFocus });
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return this.closeAll({ restoreFocus });
|
|
}
|
|
|
|
closeAll(options = {}) {
|
|
let closed = false;
|
|
const restoreFocus = options?.restoreFocus !== false;
|
|
|
|
this.popovers.forEach((entry) => {
|
|
if (entry?.popover && typeof entry.popover.close === "function") {
|
|
const result = entry.popover.close({ restoreFocus });
|
|
closed = closed || result;
|
|
}
|
|
});
|
|
return closed;
|
|
}
|
|
|
|
destroy() {
|
|
this.popovers.forEach((entry) => {
|
|
if (entry?.popover && typeof entry.popover.destroy === "function") {
|
|
entry.popover.destroy();
|
|
}
|
|
});
|
|
this.popovers.clear();
|
|
}
|
|
}
|