// retoor <retoor@molodetz.nl>
export class OverflowTabs {
constructor() {
this.instances = [];
document.querySelectorAll("[data-overflow-tabs]").forEach((root) => {
const instance = this.create(root);
if (instance) {
this.instances.push(instance);
}
});
}
create(root) {
const more = root.querySelector(".overflow-tabs-more");
const strip = root.querySelector(".overflow-tabs-strip");
if (!more || !strip) {
return null;
}
const tabs = Array.from(strip.children).filter(
(el) => !el.classList.contains("overflow-tabs-more")
);
if (!tabs.length) {
return null;
}
const state = {
root,
strip,
more,
tabs,
overflow: [],
};
state.sync = () => {
void strip.offsetWidth;
};
state.stripOverflows = () => strip.scrollWidth > strip.clientWidth + 1;
state.layout = () => {
tabs.forEach((tab) => {
tab.hidden = false;
});
more.hidden = true;
state.sync();
if (!state.stripOverflows()) {
state.overflow = [];
return;
}
more.hidden = false;
state.sync();
while (state.stripOverflows()) {
const victim = tabs
.filter((tab) => !tab.hidden && !tab.classList.contains("active"))
.pop();
if (!victim) {
break;
}
victim.hidden = true;
state.sync();
}
state.overflow = tabs.filter((tab) => tab.hidden);
if (state.overflow.length > 0 || state.stripOverflows()) {
more.hidden = false;
}
};
state.open = (event) => {
event.preventDefault();
event.stopPropagation();
const items = state.overflow.map((tab) => ({
icon: tab.dataset.menuIcon || "",
label: tab.dataset.menuLabel || tab.textContent.trim(),
onSelect: () => tab.click(),
}));
if (!items.length) {
return;
}
const rect = more.getBoundingClientRect();
window.app.contextMenu.open(rect.left, rect.bottom + 4, items);
};
root.classList.add("tabs-managed");
more.addEventListener("click", (event) => state.open(event));
for (const tab of tabs) {
tab.addEventListener("click", () => {
requestAnimationFrame(() => state.layout());
});
}
const scheduleLayout = () => {
requestAnimationFrame(() => state.layout());
};
window.addEventListener("resize", scheduleLayout);
if (typeof ResizeObserver !== "undefined") {
state.resizeObserver = new ResizeObserver(scheduleLayout);
state.resizeObserver.observe(root);
state.resizeObserver.observe(strip);
}
scheduleLayout();
if (document.fonts?.ready) {
document.fonts.ready.then(scheduleLayout);
}
window.addEventListener("load", scheduleLayout, { once: true });
return state;
}
}