WebOrbiton
v3.0.0.2

FlatlyPage

148 lines · 5.0 KB
  1. const MINIMAL_KEY = 'admin-minimalism';
  2. ​
  3. const isMinimalOn = () => document.documentElement.getAttribute('data-minimal') === 'on';
  4. ​
  5. const setMinimal = (on) => {
  6. if (on) {
  7. document.documentElement.setAttribute('data-minimal', 'on');
  8. } else {
  9. document.documentElement.removeAttribute('data-minimal');
  10. }
  11. try {
  12. localStorage.setItem(MINIMAL_KEY, on ? 'on' : 'off');
  13. } catch (error) {}
  14. };
  15. ​
  16. try {
  17. if (localStorage.getItem(MINIMAL_KEY) === 'on') {
  18. document.documentElement.setAttribute('data-minimal', 'on');
  19. }
  20. } catch (error) {}
  21. ​
  22. const GLASS_KEY = 'admin-glass-effect';
  23. ​
  24. const isGlassOn = () => document.documentElement.getAttribute('data-glass') !== 'off';
  25. ​
  26. const setGlass = (on) => {
  27. if (on) {
  28. document.documentElement.removeAttribute('data-glass');
  29. } else {
  30. document.documentElement.setAttribute('data-glass', 'off');
  31. }
  32. try {
  33. localStorage.setItem(GLASS_KEY, on ? 'on' : 'off');
  34. } catch (error) {}
  35. };
  36. ​
  37. try {
  38. if (localStorage.getItem(GLASS_KEY) === 'off') {
  39. document.documentElement.setAttribute('data-glass', 'off');
  40. }
  41. } catch (error) {}
  42. ​
  43. const THEME_KEY = 'admin-data-theme';
  44. ​
  45. const applyTheme = (theme) => {
  46. document.documentElement.setAttribute('data-theme', theme);
  47. localStorage.setItem(THEME_KEY, theme);
  48. };
  49. ​
  50. const savedTheme = localStorage.getItem(THEME_KEY);
  51. const systemTheme = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
  52. const currentTheme = savedTheme || systemTheme;
  53. ​
  54. applyTheme(currentTheme);
  55. ​
  56. const toggleTheme = () => {
  57. const activeTheme = document.documentElement.getAttribute('data-theme');
  58. const newTheme = activeTheme === 'dark' ? 'light' : 'dark';
  59. applyTheme(newTheme);
  60. };
  61. ​
  62. document.addEventListener('DOMContentLoaded', () => {
  63. const sidebar = document.querySelector('.sidebar');
  64. const nav = document.querySelector('.sidebar-nav');
  65. if (!sidebar || !nav) return;
  66. ​
  67. const MAX_VISIBLE = 4;
  68. const links = Array.from(nav.querySelectorAll('a.nav-item'));
  69. ​
  70. const avatar = sidebar.querySelector('.user-avatar');
  71. const actions = document.querySelector('.header-actions');
  72. if (avatar && actions) {
  73. const account = document.createElement('a');
  74. account.href = (window.FLATLY_BASE || '') + '/admin/account';
  75. account.className = 'mobile-avatar';
  76. account.textContent = avatar.textContent.trim();
  77. actions.appendChild(account);
  78. }
  79. ​
  80. if (links.length <= MAX_VISIBLE) return;
  81. ​
  82. const visibleCount = MAX_VISIBLE - 1;
  83. const active = links.findIndex((a) => a.classList.contains('active'));
  84. const visible = new Set(links.slice(0, visibleCount));
  85. if (active >= visibleCount) {
  86. visible.delete(links[visibleCount - 1]);
  87. visible.add(links[active]);
  88. }
  89. const overflow = links.filter((a) => !visible.has(a));
  90. overflow.forEach((a) => a.classList.add('nav-overflow-item'));
  91. ​
  92. const moreBtn = document.createElement('button');
  93. moreBtn.type = 'button';
  94. moreBtn.className = 'nav-more-btn';
  95. moreBtn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="5" cy="12" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="19" cy="12" r="1.5"/></svg><span>More</span>';
  96. ​
  97. const backdrop = document.createElement('div');
  98. backdrop.className = 'nav-sheet-backdrop';
  99. const sheet = document.createElement('div');
  100. sheet.className = 'nav-sheet';
  101. const handle = document.createElement('div');
  102. handle.className = 'nav-sheet-handle';
  103. sheet.appendChild(handle);
  104. ​
  105. overflow.forEach((a) => {
  106. const item = a.cloneNode(true);
  107. item.className = 'nav-sheet-link' + (a.classList.contains('active') ? ' active' : '');
  108. sheet.appendChild(item);
  109. });
  110. ​
  111. nav.appendChild(moreBtn);
  112. document.body.appendChild(backdrop);
  113. document.body.appendChild(sheet);
  114. ​
  115. const setOpen = (open) => {
  116. backdrop.classList.toggle('open', open);
  117. sheet.classList.toggle('open', open);
  118. moreBtn.classList.toggle('open', open);
  119. };
  120. ​
  121. moreBtn.addEventListener('click', () => setOpen(!sheet.classList.contains('open')));
  122. backdrop.addEventListener('click', () => setOpen(false));
  123. });
  124. ​
  125. (() => {
  126. const desktop = window.matchMedia('(min-width: 769px)');
  127. let app = null;
  128. ​
  129. const reset = (el) => {
  130. if (el && (el.scrollTop !== 0 || el.scrollLeft !== 0)) {
  131. el.scrollTop = 0;
  132. el.scrollLeft = 0;
  133. }
  134. };
  135. ​
  136. document.addEventListener('scroll', (e) => {
  137. if (!desktop.matches) return;
  138. const target = e.target;
  139. if (!app) app = document.querySelector('.app');
  140. if (target === document || target === document.documentElement || target === document.body || target === app || (target.classList && target.classList.contains('sidebar'))) {
  141. reset(document.scrollingElement);
  142. reset(document.body);
  143. reset(app);
  144. reset(document.querySelector('.sidebar'));
  145. }
  146. }, { capture: true, passive: true });
  147. })();
  148. ​