Vue API Dashboard (2x)

预览 Vue API

目前為 2020-05-02 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Vue API Dashboard (2x)
  3. // @namespace https://xianghongai.github.io/
  4. // @version 0.0.1
  5. // @description 预览 Vue API
  6. // @author Nicholas Hsiang / 山茶树和葡萄树
  7. // @icon https://xinlu.ink/favicon.ico
  8. // @match https://cn.vuejs.org/v2/api/*
  9. // @match https://vuejs.org/v2/api/*
  10. // @grant none
  11. // ==/UserScript==
  12. (() => {
  13.  
  14. 'use strict';
  15.  
  16. const titleText = 'Vue API Dashboard';
  17.  
  18. const gridSelector = '.sidebar-inner .menu-root';
  19. const girdIsList = false; // 如果提取的是一个 Node 数组
  20. const columnSelector = '.menu-root>li';
  21. const columnTitleSelector = '.menu-root>li>a.section-link';
  22. const menuListSelector = '.menu-sub';
  23. const menuItemSelector = '.menu-sub li';
  24.  
  25. const menuItemActionSelector = null;
  26.  
  27. const helpEnable = false;
  28. const helpSelector = '';
  29.  
  30. // 使用本扩展的样式风格,将会替换原站点的菜单风格
  31. const customStyleEnable = true; // Dark & Light
  32. const cloneNodeEnable = true; // 保留原 DOM 节点?
  33.  
  34. const compactColumnEnable = true; // 紧凑模式,将会合并一些少的列
  35. const compactColumnLimit = 13; // 多列数据组合上限
  36.  
  37. function initialExtraStyle() {
  38. return `
  39. .hs-dashboard__toggle {
  40. top: 5px;
  41. }
  42. `;
  43. }
  44.  
  45. /* ------------------------------------------------------------------------- */
  46.  
  47. let wrapperEle = null;
  48. let themeSwitchEle = null;
  49. let themeSwitchForm = null;
  50.  
  51. const bodyContainer = document.querySelector('body');
  52.  
  53. function initialDashboard() {
  54. initialToggle();
  55. initialStyle(initialExtraStyle);
  56. initialMenu(cloneNodeEnable);
  57. initialHelp();
  58. handleEvent();
  59. handleTheme(true);
  60. }
  61.  
  62. let interval = null;
  63.  
  64. function ready() {
  65. const originEle = document.querySelector(gridSelector);
  66.  
  67. if (originEle) {
  68. clearInterval(interval);
  69. // Dashboard
  70. initialDashboard();
  71. // Other
  72. }
  73. }
  74.  
  75. interval = setInterval(ready, 1000);
  76.  
  77. // #region MENU
  78. /** 生成 Menu */
  79. function initialMenu(clone) {
  80. // Wrapper
  81. wrapperEle = document.createElement('section');
  82. wrapperEle.classList.add('hs-dashboard__wrapper', 'hs-hide');
  83.  
  84. if (customStyleEnable) {
  85. wrapperEle.setAttribute('id', 'hs-dashboard');
  86. }
  87.  
  88. // Header
  89. const headerEle = document.createElement('header');
  90. headerEle.classList.add('hs-dashboard__header');
  91.  
  92. // Title → Header
  93. const titleEle = document.createElement('h1');
  94. titleEle.classList.add('hs-dashboard__title');
  95. titleEle.innerText = titleText || '';
  96. headerEle.appendChild(titleEle);
  97.  
  98. // Theme → Header
  99. if (customStyleEnable) {
  100. const themeEle = document.createElement('div');
  101. themeEle.classList.add('hs-theme-switch');
  102. themeEle.innerHTML = initialThemeTpl();
  103. headerEle.appendChild(themeEle);
  104. }
  105.  
  106. // Menu
  107. const containerEle = document.createElement('div');
  108. containerEle.classList.add('hs-dashboard__container');
  109.  
  110. // 1. 先从页面上获取 DOM 生成 gird
  111. let gridEle = null;
  112. let nodeTemp = null;
  113.  
  114. if (girdIsList) {
  115. gridEle = document.createElement('div');
  116. const gridListEle = document.querySelectorAll(gridSelector);
  117. gridListEle &&
  118. gridListEle.forEach((element) => {
  119. nodeTemp = clone ? element.cloneNode(true) : element;
  120. gridEle.appendChild(nodeTemp);
  121. });
  122. } else {
  123. nodeTemp = document.querySelector(gridSelector);
  124. gridEle = clone ? nodeTemp.cloneNode(true) : nodeTemp;
  125. gridEle && nodeTemp.removeAttribute('id');
  126. }
  127.  
  128. gridEle.classList.add('hs-dashboard__grid'); // 追加新的样式
  129.  
  130. // Menu → Container
  131. containerEle.appendChild(gridEle);
  132.  
  133. // 2. 内部元素追加新的样式
  134. // 2.1 column
  135. const columnEle = containerEle.querySelectorAll(columnSelector);
  136. columnEle.forEach((element) => {
  137. element.classList.add('hs-dashboard__column');
  138. });
  139.  
  140. // 2.2 title
  141. const columnTitleEle = containerEle.querySelectorAll(columnTitleSelector);
  142. columnTitleEle.forEach((element) => {
  143. element.classList.add('hs-dashboard__title');
  144. });
  145.  
  146. // 2.3 menu list
  147. const menuListEle = containerEle.querySelectorAll(menuListSelector);
  148. menuListEle.forEach((element) => {
  149. element.classList.add('hs-dashboard__list');
  150. });
  151.  
  152. // 2.4 menu item
  153. const menuItemEle = containerEle.querySelectorAll(menuItemSelector);
  154. menuItemEle.forEach((element) => {
  155. element.classList.add('hs-dashboard__item');
  156. });
  157.  
  158. // 2.5 menu item action
  159. if (menuItemActionSelector) {
  160. const actionEle = containerEle.querySelector(menuItemActionSelector);
  161. const menuItemTemp = getParents(actionEle, menuItemSelector);
  162. menuItemTemp.classList.add('hs-active');
  163. }
  164.  
  165. if (compactColumnEnable) {
  166. const { columns, layout } = compactColumn(
  167. containerEle,
  168. compactColumnLimit
  169. );
  170.  
  171. const ul = document.createElement('ul');
  172. ul.classList.add('hs-dashboard__grid');
  173.  
  174. Array.isArray(layout) &&
  175. layout.forEach((item) => {
  176. const li = document.createElement('li');
  177. li.classList.add('hs-dashboard__column');
  178.  
  179. if (Array.isArray(item)) {
  180. item.forEach((index) => {
  181. const columnItem = columns[index];
  182. const title = columnItem.querySelector('.hs-dashboard__title');
  183. const list = columnItem.querySelector('.hs-dashboard__list');
  184. title && li.appendChild(title);
  185. list && li.appendChild(list);
  186. });
  187. } else {
  188. const columnItem = columns[item];
  189. const title = columnItem.querySelector('.hs-dashboard__title');
  190. const list = columnItem.querySelector('.hs-dashboard__list');
  191. title && li.appendChild(title);
  192. list && li.appendChild(list);
  193. }
  194.  
  195. ul.appendChild(li);
  196. });
  197.  
  198. containerEle.removeChild(gridEle);
  199. containerEle.appendChild(ul);
  200. }
  201.  
  202. // header,container → wrapper
  203. wrapperEle.appendChild(headerEle);
  204. wrapperEle.appendChild(containerEle);
  205.  
  206. // wrapper → body
  207. bodyContainer.appendChild(wrapperEle);
  208. }
  209.  
  210. function compactColumn(containerEle, limit) {
  211. // 只能按列去查,有的列里面是没有 list 的
  212. let columns = containerEle.querySelectorAll('.hs-dashboard__column');
  213. let columnCount = []; // 相邻的数相加不超过指定值,就合并到一个新数组,将组成新的 column
  214. let layout = []; // 计算出来的新的数据布局方式
  215.  
  216. if (columns && columns.length) {
  217. columns.forEach((element) => {
  218. const listItem = element.querySelectorAll('.hs-dashboard__item');
  219. columnCount.push(listItem.length);
  220. });
  221.  
  222. /**
  223. * DESIGN NOTES
  224. *
  225. * 相邻的数相加
  226. *
  227. * 1. 将相邻的坐标存放在 arr
  228. * 2. 计算 arr 中坐标的数据量是否超过指定值
  229. * 3. 没超过,继续往 arr 推坐标
  230. * 4. 原先没超过,新的一进来就超过了,说明原先的已经到了阈值,原先的可以合并了推到布局中,但新的要记录下来,参与下一轮计算
  231. * 5. 下一个本身已经超过了阈值,看原先是否有参与计算的,然后各自推到布局中
  232. */
  233.  
  234. limit = limit || 12;
  235.  
  236. let arr = []; // 待合并的对象
  237. let acc = 0; // 累加判断是否临界
  238. const length = columnCount.length; // 是否到最后
  239.  
  240. columnCount.forEach((item, index) => {
  241. // 1. 新的值临界
  242. if (item > limit) {
  243. // 原先的是一个待合并的集合,还是只是一个单独的值
  244. if (arr.length > 1) {
  245. layout.push(arr);
  246. } else if (arr.length === 1) {
  247. layout.push(arr[0]);
  248. }
  249.  
  250. layout.push(index);
  251.  
  252. arr = [];
  253. acc = 0;
  254. } else {
  255. // 计算总的数据量
  256. acc += item;
  257.  
  258. // 总数据量临界
  259. if (acc > limit) {
  260. if (arr.length) {
  261. if (arr.length > 1) {
  262. layout.push(arr);
  263. } else {
  264. layout.push(arr[0]);
  265. }
  266. }
  267.  
  268. // 新的值参与下一次计算
  269. arr = [index];
  270. acc = item;
  271. } else {
  272. // 新的值没有临界
  273. arr.push(index);
  274. }
  275. }
  276.  
  277. if (index === length - 1 && arr.length) {
  278. layout.push(arr);
  279. }
  280. });
  281. }
  282.  
  283. return { columns, layout };
  284. }
  285. // #endregion MENU
  286.  
  287. // #region Event
  288. /** 注册(不可用)事件 */
  289. function handleEvent() {
  290. if (!wrapperEle) {
  291. wrapperEle = document.querySelector('.hs-dashboard__wrapper');
  292. }
  293.  
  294. if (!themeSwitchEle) {
  295. themeSwitchEle = document.querySelector('.hs-theme-switch');
  296. }
  297.  
  298. if (!themeSwitchForm) {
  299. themeSwitchForm = document.querySelector(
  300. '.hs-theme-switch__form-control'
  301. );
  302. }
  303.  
  304. bodyContainer.addEventListener('click', (event) => {
  305. const targetEle = event.target;
  306.  
  307. const itemEle = getParents(targetEle, '.hs-dashboard__item');
  308.  
  309. const isItem = hasClass(targetEle, 'hs-dashboard__item');
  310.  
  311. const isItemWrapper =
  312. getParents(targetEle, '.hs-dashboard__column') &&
  313. getParents(targetEle, '.hs-dashboard__list');
  314.  
  315. const isToggle =
  316. getParents(targetEle, '.hs-dashboard__toggle-menu') ||
  317. hasClass(targetEle, 'hs-dashboard__toggle-menu');
  318.  
  319. const isHelp =
  320. getParents(targetEle, '.hs-dashboard__toggle-help') ||
  321. hasClass(targetEle, 'hs-dashboard__toggle-help');
  322.  
  323. const isTheme =
  324. getParents(targetEle, '.hs-theme-switch') ||
  325. hasClass(targetEle, 'hs-theme-switch');
  326.  
  327. if (itemEle || isItem || isItemWrapper) {
  328. window.setTimeout(() => {
  329. clearStyle(wrapperEle);
  330. }, 300);
  331.  
  332. handleItemClick(itemEle, isItem, targetEle);
  333. } else if (isToggle) {
  334. wrapperEle.classList.toggle('hs-hide');
  335. bodyContainer.classList.toggle('hs-body-overflow_hide');
  336. } else if (isHelp) {
  337. clearStyle(wrapperEle);
  338. handleHelp();
  339. } else if (isTheme) {
  340. handleTheme();
  341. }
  342. });
  343. }
  344.  
  345. /** 导航点击 */
  346. function handleItemClick(itemEle, isItem, targetEle) {
  347. let itemTemp = null;
  348.  
  349. if (itemEle) {
  350. itemTemp = itemEle;
  351. } else if (isItem) {
  352. itemTemp = targetEle;
  353. }
  354.  
  355. if (itemTemp) {
  356. const items = wrapperEle.querySelectorAll('.hs-dashboard__item');
  357. items.forEach((element) => {
  358. element.classList.remove('hs-active');
  359. element.querySelector('a').classList.remove('active');
  360. });
  361. itemTemp.classList.add('hs-active');
  362. }
  363. }
  364.  
  365. /** 退出预览 */
  366. function clearStyle(wrapperEle) {
  367. wrapperEle.classList.add('hs-hide');
  368. bodyContainer.classList.remove('hs-body-overflow_hide');
  369. }
  370. // #endregion Event
  371.  
  372. // #region HELP
  373. /** 是否启用‘页面滚动至指定位置’ */
  374. function initialHelp() {
  375. if (!helpEnable) {
  376. const ele = document.querySelector('.hs-dashboard__toggle-help');
  377. ele.classList.add('hs-hide');
  378. }
  379. }
  380.  
  381. /** 页面滚动至指定位置 */
  382. function handleHelp() {
  383. if (!helpSelector) {
  384. return false;
  385. }
  386.  
  387. const helpEle = document.querySelector(helpSelector);
  388. const top = helpEle.getBoundingClientRect().top + window.pageYOffset;
  389.  
  390. window.scrollTo({
  391. top,
  392. behavior: 'smooth',
  393. });
  394. }
  395. // #endregion HELP
  396.  
  397. // #region STYLE
  398. /** 添加样式 */
  399. function initialStyle(param) {
  400. let tpl = initialStyleTpl();
  401. const headEle = document.head || document.getElementsByTagName('head')[0];
  402. const styleEle = document.createElement('style');
  403.  
  404. let str = null;
  405.  
  406. if (typeof param === 'function') {
  407. str = param();
  408. } else if (typeof param === 'string') {
  409. str = param;
  410. }
  411.  
  412. if (typeof str === 'string') {
  413. tpl += str;
  414. }
  415.  
  416. styleEle.type = 'text/css';
  417.  
  418. if (styleEle.styleSheet) {
  419. styleEle.styleSheet.cssText = tpl;
  420. } else {
  421. styleEle.appendChild(document.createTextNode(tpl));
  422. }
  423.  
  424. headEle.appendChild(styleEle);
  425. }
  426.  
  427. /** 样式表 */
  428. function initialStyleTpl() {
  429. return `
  430.  
  431. :root {
  432. --item-height: 36px;
  433. --hs-font-size-base: 15px;
  434. --hs-global-spacing: 1rem;
  435. --hs-color-primary: #1890ff;
  436. --hs-spacing-horizontal: var(--hs-global-spacing);
  437.  
  438. --hs-color-white: #fff;
  439. --hs-color-black: #000;
  440. --hs-color-gray-0: var(--hs-color-white);
  441. --hs-color-gray-100: #f5f6f7;
  442. --hs-color-gray-200: #ebedf0;
  443. --hs-color-gray-300: #dadde1;
  444. --hs-color-gray-400: #ccd0d5;
  445. --hs-color-gray-500: #bec3c9;
  446. --hs-color-gray-600: #8d949e;
  447. --hs-color-gray-700: #606770;
  448. --hs-color-gray-800: #444950;
  449. --hs-color-gray-900: #1c1e21;
  450. --hs-color-gray-1000: var(--hs-color-black);
  451. --hs-color-emphasis-0: var(--hs-color-gray-0);
  452. --hs-color-emphasis-100: var(--hs-color-gray-100);
  453. --hs-color-emphasis-200: var(--hs-color-gray-200);
  454. --hs-color-emphasis-300: var(--hs-color-gray-300);
  455. --hs-color-emphasis-400: var(--hs-color-gray-400);
  456. --hs-color-emphasis-500: var(--hs-color-gray-500);
  457. --hs-color-emphasis-600: var(--hs-color-gray-600);
  458. --hs-color-emphasis-700: var(--hs-color-gray-700);
  459. --hs-color-emphasis-800: var(--hs-color-gray-800);
  460. --hs-color-emphasis-900: var(--hs-color-gray-900);
  461. --hs-color-emphasis-1000: var(--hs-color-gray-1000);
  462. }
  463. .hs-hide {
  464. display: none !important;
  465. }
  466.  
  467. .hs-body-overflow_hide {
  468. height: 100% !important;
  469. overflow: hidden !important;
  470. }
  471.  
  472. /* #region toggle */
  473. .hs-dashboard__toggle {
  474. position: fixed;
  475. z-index: 99999;
  476. top: 15px;
  477. right: 5px;
  478. }
  479.  
  480. .hs-dashboard__toggle-item {
  481. position: relative;
  482. width: 28px;
  483. height: 28px;
  484. margin-top: 10px;
  485. margin-bottom: 10px;
  486. overflow: hidden;
  487. line-height: 1 !important;
  488. border-radius: 50%;
  489. border: 1px solid #ccc;
  490. text-align: center;
  491. color: #555;
  492. background-color: #fff;
  493. cursor: pointer;
  494. transition: all 0.2s;
  495. }
  496.  
  497. .hs-dashboard__toggle-item:hover {
  498. border-color: #aaa;
  499. color: #111;
  500. }
  501.  
  502. .hs-dashboard__toggle-icon svg{
  503. position: absolute;
  504. top: 50%;
  505. left: 50%;
  506. z-index: 9;
  507. transform: translate(-50%, -50%);
  508. font-style: normal !important;
  509. }
  510. /* #endregion toggle */
  511.  
  512. /* #region wrapper */
  513. .hs-dashboard__wrapper {
  514. position: fixed;
  515. top: 0;
  516. right: 0;
  517. bottom: 0;
  518. left: 0;
  519. z-index: 99998;
  520. overflow-y: auto;
  521. background-color: #fff;
  522. font-size: var(--hs-font-size-base);
  523. }
  524.  
  525. .hs-dashboard__wrapper::-webkit-scrollbar {
  526. width: 8px;
  527. height: 6px;
  528. background: rgba(0, 0, 0, 0.1);
  529. }
  530.  
  531. .hs-dashboard__wrapper::-webkit-scrollbar-thumb {
  532. background: rgba(0, 0, 0, 0.3);
  533. }
  534.  
  535. .hs-dashboard__wrapper::-webkit-scrollbar-track {
  536. background: rgba(0, 0, 0, 0.1);
  537. }
  538. /* #endregion wrapper */
  539.  
  540. .hs-dashboard__header {
  541. position: relative;
  542. padding-top: 10px;
  543. text-align: center;
  544. }
  545.  
  546. .hs-dashboard__header .hs-dashboard__title {
  547. margin: 0;
  548. padding-top: 10px;
  549. padding-bottom: 10px;
  550. font-size: 1em;
  551. font-weight: normal;
  552. }
  553.  
  554. /* #region theme */
  555. .hs-theme-switch {
  556. display: flex;
  557. touch-action: pan-x;
  558. position: relative;
  559. background-color: #fff;
  560. border: 0;
  561. margin: 0;
  562. padding: 0;
  563. user-select: none;
  564. -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  565. -webkit-tap-highlight-color: transparent;
  566. cursor: pointer;
  567. }
  568.  
  569. .hs-theme-switch {
  570. width: 50px;
  571. height: 24px;
  572. padding: 0;
  573. border-radius: 30px;
  574. background-color: #4d4d4d;
  575. transition: all 0.2s ease;
  576. }
  577.  
  578. .hs-dashboard__header .hs-theme-switch {
  579. position: absolute;
  580. top: 10px;
  581. left: 10px;
  582. }
  583.  
  584. .hs-theme-switch__style {
  585. position: relative;
  586. width: 24px;
  587. height: 24px;
  588. line-height: 1;
  589. font-size: 20px;
  590. text-align: center;
  591. }
  592.  
  593. .hs-theme-switch__icon svg {
  594. position: absolute;
  595. top: 50%;
  596. left: 50%;
  597. transform: translate(-50%, -50%);
  598. }
  599.  
  600. .hs-theme-switch__thumb {
  601. position: absolute;
  602. top: 1px;
  603. left: 1px;
  604. width: 22px;
  605. height: 22px;
  606. border: 1px solid #ff7938;
  607. border-radius: 50%;
  608. background-color: #fafafa;
  609. box-sizing: border-box;
  610. transition: all 0.25s ease;
  611. }
  612.  
  613. .hs-theme-switch_checked .hs-theme-switch__thumb {
  614. left: 27px;
  615. border-color: #4d4d4d;
  616. }
  617.  
  618. .hs-toggle-screenreader-only {
  619. border: 0;
  620. clip: rect(0 0 0 0);
  621. height: 1px;
  622. margin: -1px;
  623. overflow: hidden;
  624. padding: 0;
  625. position: absolute;
  626. width: 1px;
  627. }
  628. /* #endregion theme */
  629.  
  630. /* #region grid */
  631. .hs-dashboard__grid {
  632. display: flex;
  633. justify-content: space-around;
  634. margin: 0;
  635. padding: 0 40px;
  636. list-style: none;
  637. }
  638.  
  639. .hs-dashboard__column {
  640. padding-right: 10px;
  641. padding-left: 10px;
  642. }
  643.  
  644. .hs-dashboard__column a {
  645. display: block;
  646. padding-left: 20px !important;
  647. padding-right: 40px !important;
  648. text-decoration: none;
  649. }
  650.  
  651. .hs-dashboard__container ul:not(.hs-dashboard__grid) {
  652. padding: 0;
  653. }
  654.  
  655. .hs-dashboard__container li {
  656. padding-left: 0 !important;
  657. list-style: none;
  658. }
  659.  
  660. .hs-dashboard__column .hs-dashboard__title {
  661. display: block;
  662. padding-left: var(--hs-spacing-horizontal) !important;
  663. padding-right: calc(var(--hs-spacing-horizontal) * 2) !important;
  664. text-align: left;
  665. margin-top: 10px !important;
  666. }
  667.  
  668. .hs-dashboard__column .hs-dashboard__list {
  669. margin-top: 10px !important;
  670. }
  671.  
  672. .hs-dashboard__column .hs-dashboard__list+.hs-dashboard__title {
  673. margin-top: var(--hs-global-spacing);
  674. padding-top: var(--hs-global-spacing);
  675. }
  676.  
  677. .hs-dashboard__column .hs-dashboard__list .hs-dashboard__item {
  678. margin: 0 !important;
  679. padding-left: 0 !important;
  680. padding-right: 0 !important;
  681. height: var(--item-height);
  682. line-height: var(--item-height);
  683. }
  684. /* #endregion grid */
  685.  
  686. /* #region custom */
  687. #hs-dashboard.hs-dashboard__wrapper {
  688. transition: all 0.2s ease;
  689. }
  690.  
  691. #hs-dashboard .hs-dashboard__column .hs-dashboard__title {
  692. font-size: 14px;
  693. line-height: 1.5715;
  694. color: rgba(0, 0, 0, 0.45);
  695. }
  696.  
  697. #hs-dashboard a {
  698. overflow: hidden;
  699. white-space: nowrap;
  700. font-size: 14px;
  701. text-overflow: ellipsis;
  702. text-decoration: none;
  703. color: rgba(0, 0, 0, 0.85);
  704. transition: color 0.3s ease;
  705. }
  706.  
  707. #hs-dashboard a:hover {
  708. color: var(--hs-color-primary);
  709. text-decoration: none;
  710. outline: 0;
  711. }
  712.  
  713. /* light */
  714. #hs-dashboard.hs-dashboard__wrapper_light {
  715. color: #161616;
  716. background-color: #fff;
  717. }
  718.  
  719. #hs-dashboard.hs-dashboard__wrapper_light .hs-dashboard__list+.hs-dashboard__title {
  720. border-top: 1px solid var(--hs-color-gray-300);
  721. }
  722.  
  723. /* dark */
  724. #hs-dashboard.hs-dashboard__wrapper_dark {
  725. color: #fff;
  726. background-color: #161616;
  727. }
  728.  
  729. #hs-dashboard.hs-dashboard__wrapper_dark .hs-dashboard__list+.hs-dashboard__title {
  730. border-top: 1px solid var(--hs-color-gray-600);
  731. }
  732.  
  733. #hs-dashboard.hs-dashboard__wrapper_dark .hs-dashboard__title {
  734. font-weight: bold;
  735. color: #fff;
  736. }
  737.  
  738. #hs-dashboard.hs-dashboard__wrapper_dark a {
  739. color: #fff;
  740. }
  741.  
  742. #hs-dashboard.hs-dashboard__wrapper_dark a:hover {
  743. color: var(--hs-color-primary);
  744. }
  745.  
  746. #hs-dashboard .hs-dashboard__item.active,
  747. #hs-dashboard .hs-dashboard__item.active a,
  748. #hs-dashboard .hs-dashboard__item .active,
  749. #hs-dashboard .hs-dashboard__item.hs-active,
  750. #hs-dashboard .hs-dashboard__item.hs-active a {
  751. color: var(--hs-color-primary);
  752. }
  753.  
  754. #hs-dashboard .hs-dashboard__item.hs-active {
  755. background-color: #e6f7ff;
  756. }
  757.  
  758. #hs-dashboard .hs-dashboard__item {
  759. position: relative;
  760. }
  761.  
  762. #hs-dashboard .hs-dashboard__item::after {
  763. content: ' ';
  764. position: absolute;
  765. top: 0;
  766. right: 0;
  767. bottom: 0;
  768. border-right: 3px solid var(--hs-color-primary);
  769. transform: scaleY(0.0001);
  770. transition: transform 0.15s cubic-bezier(0.215, 0.61, 0.355, 1),
  771. opacity 0.15s cubic-bezier(0.215, 0.61, 0.355, 1),
  772. -webkit-transform 0.15s cubic-bezier(0.215, 0.61, 0.355, 1);
  773. opacity: 0;
  774. }
  775.  
  776. #hs-dashboard .hs-dashboard__item.hs-active::after {
  777. transform: scaleY(1);
  778. opacity: 1;
  779. transition: transform 0.15s cubic-bezier(0.645, 0.045, 0.355, 1),
  780. opacity 0.15s cubic-bezier(0.645, 0.045, 0.355, 1),
  781. -webkit-transform 0.15s cubic-bezier(0.645, 0.045, 0.355, 1);
  782. }
  783. /* #endregion custom */
  784.  
  785. `;
  786. }
  787. // #endregion STYLE
  788.  
  789. // #region TOGGLE
  790. /** 生成 Dashboard 开关 */
  791. function initialToggle() {
  792. const tpl = initialToggleTpl();
  793. const ele = document.createElement('section');
  794. // ele.className = 'hs-dashboard__toggle';
  795. // ele.setAttribute("class", "hs-dashboard__toggle");
  796. ele.classList.add('hs-dashboard__toggle');
  797. ele.innerHTML = tpl;
  798.  
  799. // toggle → body
  800. bodyContainer.appendChild(ele);
  801. }
  802. /** Dashboard 开关 DOM */
  803. function initialToggleTpl() {
  804. return `
  805. <!-- menu -->
  806. <div class="hs-dashboard__toggle-item hs-dashboard__toggle-menu">
  807. <i class="hs-dashboard__toggle-icon">
  808. <svg
  809. viewBox="64 64 896 896"
  810. focusable="false"
  811. data-icon="appstore"
  812. width="1em"
  813. height="1em"
  814. fill="currentColor"
  815. aria-hidden="true"
  816. >
  817. <path
  818. d="M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z"
  819. ></path>
  820. </svg>
  821. </i>
  822. </div>
  823. <!-- api -->
  824. <div class="hs-dashboard__toggle-item hs-dashboard__toggle-help">
  825. <i class="hs-dashboard__toggle-icon">
  826. <svg
  827. viewBox="64 64 896 896"
  828. focusable="false"
  829. class=""
  830. data-icon="bulb"
  831. width="1em"
  832. height="1em"
  833. fill="currentColor"
  834. aria-hidden="true"
  835. >
  836. <path
  837. d="M632 888H392c-4.4 0-8 3.6-8 8v32c0 17.7 14.3 32 32 32h192c17.7 0 32-14.3 32-32v-32c0-4.4-3.6-8-8-8zM512 64c-181.1 0-328 146.9-328 328 0 121.4 66 227.4 164 284.1V792c0 17.7 14.3 32 32 32h264c17.7 0 32-14.3 32-32V676.1c98-56.7 164-162.7 164-284.1 0-181.1-146.9-328-328-328zm127.9 549.8L604 634.6V752H420V634.6l-35.9-20.8C305.4 568.3 256 484.5 256 392c0-141.4 114.6-256 256-256s256 114.6 256 256c0 92.5-49.4 176.3-128.1 221.8z"
  838. ></path>
  839. </svg>
  840. </i>
  841. </div>
  842. `;
  843. }
  844. // #endregion TOGGLE
  845.  
  846. // #region THEME
  847. function handleTheme(isInit) {
  848. if (isInit) {
  849. const theme = localStorage.getItem('hs_dashboard_theme');
  850.  
  851. if (theme && theme === 'dark') {
  852. themeSwitchForm.checked = true;
  853. } else {
  854. themeSwitchForm.checked = false;
  855. }
  856. } else {
  857. themeSwitchForm.click();
  858. }
  859.  
  860. const checked = themeSwitchForm.checked;
  861.  
  862. if (checked) {
  863. localStorage.setItem('hs_dashboard_theme', 'dark');
  864. wrapperEle.classList.add('hs-dashboard__wrapper_dark');
  865. wrapperEle.classList.remove('hs-dashboard__wrapper_light');
  866. themeSwitchEle.classList.add('hs-theme-switch_checked');
  867. } else {
  868. localStorage.setItem('hs_dashboard_theme', 'light');
  869. wrapperEle.classList.add('hs-dashboard__wrapper_light');
  870. wrapperEle.classList.remove('hs-dashboard__wrapper_dark');
  871. themeSwitchEle.classList.remove('hs-theme-switch_checked');
  872. }
  873. }
  874.  
  875. function initialThemeTpl() {
  876. return `
  877. <input type="checkbox" class="hs-toggle-screenreader-only hs-theme-switch__form-control" title="Dark mode" />
  878. <div class="hs-theme-switch__style hs-theme-switch__style_dark">
  879. <i class="hs-theme-switch__icon">
  880. <svg
  881. t="1588325093630"
  882. class="icon"
  883. viewBox="0 0 1024 1024"
  884. version="1.1"
  885. xmlns="http://www.w3.org/2000/svg"
  886. p-id="11008"
  887. width="1em"
  888. height="1em"
  889. >
  890. <path
  891. d="M483.555556 964.266667c-164.977778 0-315.733333-85.333333-398.222223-224.711111 19.911111 2.844444 39.822222 2.844444 56.888889 2.844444 275.911111 0 500.622222-224.711111 500.622222-500.622222 0-68.266667-14.222222-133.688889-39.822222-193.422222 201.955556 54.044444 347.022222 238.933333 347.022222 449.422222 0 256-210.488889 466.488889-466.488888 466.488889z"
  892. fill="#F7FF53"
  893. p-id="11009"
  894. ></path>
  895. <path
  896. d="M631.466667 73.955556c179.2 62.577778 301.511111 230.4 301.511111 423.822222 0 247.466667-201.955556 449.422222-449.422222 449.422222-147.911111 0-281.6-71.111111-364.088889-187.733333H142.222222c284.444444 0 517.688889-233.244444 517.688889-517.688889 0-56.888889-8.533333-113.777778-28.444444-167.822222M571.733333 22.755556C605.866667 88.177778 625.777778 162.133333 625.777778 241.777778c0 267.377778-216.177778 483.555556-483.555556 483.555555-31.288889 0-59.733333-2.844444-88.177778-8.533333 79.644444 156.444444 241.777778 264.533333 429.511112 264.533333 267.377778 0 483.555556-216.177778 483.555555-483.555555C967.111111 261.688889 796.444444 65.422222 571.733333 22.755556z"
  897. fill="#303133"
  898. p-id="11010"
  899. ></path>
  900. <path
  901. d="M787.911111 455.111111c-5.688889-2.844444-8.533333-8.533333-5.688889-14.222222 5.688889-17.066667-2.844444-42.666667-19.911111-48.355556-17.066667-5.688889-39.822222 8.533333-45.511111 22.755556-2.844444 5.688889-8.533333 8.533333-14.222222 5.688889-5.688889-2.844444-8.533333-8.533333-5.688889-14.222222 8.533333-25.6 42.666667-45.511111 73.955555-34.133334 28.444444 11.377778 39.822222 48.355556 31.288889 73.955556-2.844444 5.688889-8.533333 8.533333-14.222222 8.533333"
  902. fill="#303133"
  903. p-id="11011"
  904. ></path>
  905. <path
  906. d="M608.711111 620.088889c-14.222222 0-28.444444-2.844444-39.822222-11.377778-31.288889-22.755556-31.288889-65.422222-31.288889-68.266667 0-8.533333 8.533333-17.066667 17.066667-17.066666s17.066667 8.533333 17.066666 17.066666 2.844444 31.288889 17.066667 39.822223c11.377778 8.533333 25.6 8.533333 45.511111 0 8.533333-2.844444 19.911111 2.844444 22.755556 11.377777 2.844444 8.533333-2.844444 19.911111-11.377778 22.755556-14.222222 2.844444-25.6 5.688889-36.977778 5.688889zM571.733333 540.444444z"
  907. fill="#FF2929"
  908. p-id="11012"
  909. ></path>
  910. <path
  911. d="M810.666667 588.8c-5.688889 19.911111-36.977778 28.444444-68.266667 19.911111-31.288889-8.533333-54.044444-34.133333-48.355556-54.044444 5.688889-19.911111 36.977778-28.444444 68.266667-19.911111 34.133333 11.377778 54.044444 34.133333 48.355556 54.044444"
  912. fill="#FFA450"
  913. p-id="11013"
  914. ></path>
  915. <path
  916. d="M864.711111 270.222222c14.222222 42.666667 19.911111 91.022222 19.911111 136.533334 0 258.844444-213.333333 466.488889-477.866666 466.488888-96.711111 0-187.733333-28.444444-264.533334-76.8 82.488889 93.866667 204.8 156.444444 344.177778 156.444445C736.711111 952.888889 938.666667 756.622222 938.666667 512c0-88.177778-28.444444-173.511111-73.955556-241.777778z"
  917. fill="#FF7938"
  918. p-id="11014"
  919. ></path>
  920. </svg>
  921. </i>
  922. </div>
  923. <div class="hs-theme-switch__style hs-theme-switch__style_light">
  924. <i class="hs-theme-switch__icon">
  925. <svg
  926. t="1588324703446"
  927. class="icon"
  928. viewBox="0 0 1024 1024"
  929. version="1.1"
  930. xmlns="http://www.w3.org/2000/svg"
  931. p-id="6232"
  932. width="1em"
  933. height="1em"
  934. >
  935. <path
  936. d="M792.35 835.94l-128.09-30.32c-17.73-4.2-36.12 3.66-45.34 19.37l-66.64 113.52c-15.83 26.97-54.67 27.4-71.1 0.79l-69.14-112.02c-9.57-15.5-28.13-22.95-45.76-18.36l-127.39 33.15c-30.26 7.88-58.03-19.29-50.83-49.72l30.32-128.09c4.2-17.73-3.66-36.12-19.37-45.34L85.49 552.28c-26.97-15.83-27.4-54.67-0.79-71.1l112.02-69.14c15.5-9.57 22.95-28.13 18.36-45.76l-33.15-127.39c-7.88-30.26 19.29-58.03 49.72-50.83l128.09 30.32c17.73 4.2 36.12-3.66 45.34-19.37l66.64-113.52c15.83-26.97 54.67-27.4 71.1-0.79l69.14 112.02c9.57 15.5 28.13 22.95 45.76 18.36l127.39-33.15c30.26-7.88 58.03 19.29 50.83 49.72l-30.32 128.09c-4.2 17.73 3.66 36.12 19.37 45.34l113.52 66.64c26.97 15.83 27.4 54.67 0.79 71.1l-112.02 69.14c-15.5 9.57-22.95 28.13-18.36 45.76l33.15 127.39c7.88 30.26-19.29 58.03-49.72 50.83z"
  937. fill="#FF7938"
  938. p-id="6233"
  939. ></path>
  940. <path
  941. d="M512 512m-207.66 0a207.66 207.66 0 1 0 415.32 0 207.66 207.66 0 1 0-415.32 0Z"
  942. fill="#F7FF53"
  943. p-id="6234"
  944. ></path>
  945. <path
  946. d="M442.78 468.74m-25.96 0a25.96 25.96 0 1 0 51.92 0 25.96 25.96 0 1 0-51.92 0Z"
  947. fill="#303133"
  948. p-id="6235"
  949. ></path>
  950. <path
  951. d="M581.22 468.74m-25.96 0a25.96 25.96 0 1 0 51.92 0 25.96 25.96 0 1 0-51.92 0Z"
  952. fill="#303133"
  953. p-id="6236"
  954. ></path>
  955. <path
  956. d="M442.78 582.02s17.31 48.31 69.22 48.31 69.22-48.31 69.22-48.31H442.78z"
  957. fill="#FF2929"
  958. p-id="6237"
  959. ></path>
  960. </svg>
  961. </i>
  962. </div>
  963. <div class="hs-theme-switch__thumb"></div>
  964. `;
  965. }
  966. // #endregion THEME
  967.  
  968. // #region COMMON
  969. function hasClass(el, className) {
  970. if (el.classList) {
  971. return el.classList.contains(className);
  972. } else {
  973. return !!el.className.match(
  974. new RegExp('(\\s|^)' + className + '(\\s|$)')
  975. );
  976. }
  977. }
  978.  
  979. function getParents(elem, selector) {
  980. // Element.matches() polyfill
  981. if (!Element.prototype.matches) {
  982. Element.prototype.matches =
  983. Element.prototype.matchesSelector ||
  984. Element.prototype.mozMatchesSelector ||
  985. Element.prototype.msMatchesSelector ||
  986. Element.prototype.oMatchesSelector ||
  987. Element.prototype.webkitMatchesSelector ||
  988. function (s) {
  989. var matches = (this.document || this.ownerDocument).querySelectorAll(
  990. s
  991. ),
  992. i = matches.length;
  993. while (--i >= 0 && matches.item(i) !== this) {}
  994. return i > -1;
  995. };
  996. }
  997.  
  998. // Get the closest matching element
  999. for (; elem && elem !== document; elem = elem.parentNode) {
  1000. if (elem.matches(selector)) return elem;
  1001. }
  1002. return null;
  1003. }
  1004.  
  1005. function queryDirectChildren(parent, selector) {
  1006. const nodes = parent.querySelectorAll(selector);
  1007. const filteredNodes = [].slice
  1008. .call(nodes)
  1009. .filter(
  1010. (item) => item.parentNode.closest(selector) === parent.closest(selector)
  1011. );
  1012. return filteredNodes;
  1013. }
  1014. // #endregion
  1015. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址