Update
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user