|
// 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;
|
|
}
|
|
} |