tabs-more.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. (function () {
  2. const KEEP = 9; // 显示前 9 个
  3. const qs = (s, r = document) => r.querySelector(s);
  4. const qsa = (s, r = document) => Array.from(r.querySelectorAll(s));
  5. function ensureMoreItem(list) {
  6. let more = qs(".md-tabs__item--more", list);
  7. if (!more) {
  8. more = document.createElement("li");
  9. more.className = "md-tabs__item md-tabs__item--more";
  10. more.innerHTML = `
  11. <button class="md-tabs__link md-tabs__more-link" type="button" aria-label="更多">
  12. <span class="md-tabs__more-triangle" aria-hidden="true"></span>
  13. </button>
  14. `;
  15. }
  16. return more;
  17. }
  18. // 全局浮层(挂 body,避免被 overflow 裁剪)
  19. function ensureGlobalPanel() {
  20. let panel = qs("#md-tabs-more-global-panel");
  21. if (panel) return panel;
  22. panel = document.createElement("div");
  23. panel.id = "md-tabs-more-global-panel";
  24. panel.style.position = "fixed";
  25. panel.style.display = "none";
  26. panel.style.zIndex = "99999";
  27. panel.style.minWidth = "160px";
  28. panel.style.maxWidth = "240px";
  29. panel.style.maxHeight = "60vh";
  30. panel.style.overflow = "auto";
  31. panel.style.padding = "6px";
  32. panel.style.borderRadius = "10px";
  33. panel.style.boxShadow = "0 10px 30px rgba(0,0,0,.18)";
  34. panel.style.border = "1px solid rgba(0,0,0,.12)";
  35. panel.style.background = "#fff";
  36. panel.style.fontSize = "16px"; // ✅ 下拉字体 16px
  37. panel.style.lineHeight = "1.4";
  38. panel.style.padding = "8px";
  39. document.body.appendChild(panel);
  40. return panel;
  41. }
  42. function setPanelTheme(panel) {
  43. const slate = document.documentElement.getAttribute("data-md-color-scheme") === "slate";
  44. panel.style.background = slate ? "#1e1e1e" : "#fff";
  45. panel.style.borderColor = slate ? "rgba(255,255,255,.12)" : "rgba(0,0,0,.12)";
  46. }
  47. function closePanel(panel) {
  48. panel.style.display = "none";
  49. panel.innerHTML = "";
  50. }
  51. function openPanel(panel, anchorEl) {
  52. const r = anchorEl.getBoundingClientRect();
  53. const margin = 6;
  54. // 1) 先把面板放到一个可测量状态(不可见但参与布局)
  55. panel.style.visibility = "hidden";
  56. panel.style.display = "block";
  57. // 2) 读真实宽度
  58. const panelWidth = panel.getBoundingClientRect().width;
  59. // 3) 按“右对齐”计算:面板右边缘 = 三角按钮右边缘
  60. let left = Math.round(r.right - panelWidth);
  61. let top = Math.round(r.bottom + margin);
  62. // 4) 边界保护(避免出屏)
  63. const minLeft = 8;
  64. const maxLeft = window.innerWidth - panelWidth - 8;
  65. left = Math.min(Math.max(left, minLeft), maxLeft);
  66. // 5) 设置最终位置并显示
  67. panel.style.left = `${left}px`;
  68. panel.style.top = `${top}px`;
  69. panel.style.visibility = "visible"; // 恢复可见
  70. panel.style.display = "block";
  71. }
  72. function render() {
  73. const mdTabs = qs(".md-tabs");
  74. if (!mdTabs) return;
  75. const list = qs(".md-tabs__list", mdTabs);
  76. if (!list) return;
  77. // 真实 tabs(排除 more)
  78. const items = qsa(".md-tabs__item", list).filter(it => !it.classList.contains("md-tabs__item--more"));
  79. if (!items.length) return;
  80. // 1) 先恢复显示
  81. items.forEach(it => it.style.display = "");
  82. // 2) 插入 more 到第 9 个后面(固定位置)
  83. const more = ensureMoreItem(list);
  84. const insertBefore = items[KEEP] || null; // 原第10个(index=9)
  85. if (more.parentElement !== list) list.insertBefore(more, insertBefore);
  86. else list.insertBefore(more, insertBefore); // 重新定位(防止被 append 到末尾)
  87. // 3) 从第 10 个起隐藏
  88. const hidden = items.slice(KEEP);
  89. if (!hidden.length) {
  90. more.style.display = "none";
  91. return;
  92. }
  93. more.style.display = "";
  94. hidden.forEach(it => it.style.display = "none");
  95. // 4) 面板构建(挂到 body)
  96. const btn = qs(".md-tabs__more-link", more);
  97. const panel = ensureGlobalPanel();
  98. setPanelTheme(panel);
  99. // 防重复绑定
  100. if (!btn.dataset.bound) {
  101. btn.dataset.bound = "1";
  102. document.addEventListener("click", (e) => {
  103. const clickBtn = e.target.closest(".md-tabs__more-link");
  104. const insidePanel = e.target.closest("#md-tabs-more-global-panel");
  105. if (clickBtn) {
  106. e.preventDefault();
  107. e.stopPropagation();
  108. // 重建面板内容(保持顺序)
  109. panel.innerHTML = "";
  110. setPanelTheme(panel);
  111. const active = qs(".md-tabs__item--active", list);
  112. hidden.forEach(it => {
  113. const link = qs("a.md-tabs__link", it);
  114. if (!link) return;
  115. const a = document.createElement("a");
  116. a.href = link.href;
  117. a.textContent = link.textContent.trim();
  118. a.style.display = "block";
  119. a.style.padding = "8px 10px";
  120. a.style.borderRadius = "8px";
  121. a.style.textDecoration = "none";
  122. a.style.whiteSpace = "nowrap";
  123. a.style.color = "inherit";
  124. if (it === active) {
  125. a.style.color = "#0183FA";
  126. a.style.fontWeight = "600";
  127. a.style.background = "rgba(1,131,250,0.10)";
  128. } else {
  129. a.addEventListener("mouseenter", () => {
  130. a.style.color = "#0183FA";
  131. a.style.background = "rgba(1,131,250,0.08)";
  132. });
  133. a.addEventListener("mouseleave", () => {
  134. a.style.color = "inherit";
  135. a.style.background = "transparent";
  136. });
  137. }
  138. a.addEventListener("click", () => closePanel(panel));
  139. panel.appendChild(a);
  140. });
  141. // toggle
  142. if (panel.style.display === "block") closePanel(panel);
  143. else openPanel(panel, clickBtn);
  144. return;
  145. }
  146. if (insidePanel) return;
  147. closePanel(panel);
  148. }, true);
  149. document.addEventListener("keydown", (e) => {
  150. if (e.key === "Escape") closePanel(panel);
  151. });
  152. window.addEventListener("resize", () => closePanel(panel));
  153. window.addEventListener("scroll", () => closePanel(panel), true);
  154. }
  155. }
  156. function boot() { setTimeout(render, 0); }
  157. document.addEventListener("DOMContentLoaded", boot);
  158. if (window.document$ && typeof window.document$.subscribe === "function") {
  159. window.document$.subscribe(boot);
  160. }
  161. })();