Hi, Element Plus Component Dashboard🚀

Hi, Element Plus Component Dashboard

目前为 2025-03-26 提交的版本。查看 最新版本

// ==UserScript==
// @name         Hi, Element Plus Component Dashboard🚀
// @namespace    https://github.com/xianghongai/Tampermonkey-UserScript
// @version      1.0.1
// @description  Hi, Element Plus Component Dashboard
// @author       Nicholas Hsiang
// @match        *://element-plus.org/*
// @icon         https://element-plus.org/images/element-plus-logo-small.svg
// @grant        GM_addStyle
// @grant        GM_info
// @run-at       document-end
// @grant        unsafeWindow
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';
  console.log(GM_info.script.name);

  const logoSelector = '.logo-container img.logo';
  const menuSelector = '.sidebar';
  let wrapperEl = null;

  main();

  /**
   * Main function to execute when the script is loaded.
   */
  function main() {
    ready(() => {
      poll(menuSelector, handler, 500);
    });
  }

  /**
   * Handle the target element.
   */
  function handler() {
    toggle();
  }

  /**
   * Toggle the target element.
   */
  function toggle() {
    const closeSpan = document.createElement('span');
    closeSpan.className = 'x-toggle';
    closeSpan.innerHTML = icon();

    closeSpan.addEventListener('click', (event) => {
      if (event.shiftKey) {
        wrapperEl.removeAttribute('id');
        wrapperEl.style.display = 'grid';
        return;
      }

      if (!wrapperEl || wrapperEl.id !== 'x-menu-wrapper') {
        wrapperEl = dashboard();
        click(wrapperEl, '.link');
        return;
      }
      wrapperEl.style.display = wrapperEl.style.display === 'none' ? 'grid' : 'none';
    });
    document.body.appendChild(closeSpan);
  }

  /**
   * Click the target element.
   * @param {Element} currentElement - The target element
   * @param {string} selector - The selector of the target element
   */
  function click(currentElement, selector) {
    currentElement.addEventListener('click', (event) => {
      if (matches(event.target, selector)) {
        currentElement.style.display = 'none';
      }
    });
  }

  /**
   * Create the dashboard element.
   * @returns {Element} - The dashboard element
   */
  function dashboard() {
    wrapperEl = document.querySelector(menuSelector);
    wrapperEl.setAttribute('id', 'x-menu-wrapper');

    // 获取所有 sidebar-group 元素(排除第一个)
    const groupSelector = '.sidebar-group:not(:first-child)';
    const groupEl = Array.from(wrapperEl.querySelectorAll(groupSelector));

    const lengths = [];

    groupEl.forEach((item) => {
      const itemSelector = 'a.link';
      const itemEl = Array.from(item.querySelectorAll(itemSelector));
      const length = itemEl.length;
      const titleSelector = '.sidebar-group__title';
      const titleEl = item.querySelector(titleSelector);
      const title = titleEl.textContent;
      titleEl.textContent = `${title} (${length})`;
      lengths.push(length);
    });

    const sum = lengths.reduce((acc, curr) => acc + curr, 0);
    const sumText = `🚀 共有组件 ${sum} 个`;
    const logoEl = document.querySelector(logoSelector);
    if (logoEl) {
      logoEl.title = sumText;
    }
    console.log(sumText);
    return wrapperEl;
  }

  /**
   * Execute a function when the document is ready.
   * @param {function} eventHandler - Function to execute when the document is ready
   */
  function ready(eventHandler) {
    if (document.readyState !== 'loading') {
      eventHandler();
    } else {
      document.addEventListener('DOMContentLoaded', eventHandler);
    }
  }

  /**
   * Wait for an element to be found on the page using polling.
   * @param {string} selector - CSS selector for the element to wait for
   * @param {function} callback - Function to execute when the element is found
   * @param {number} maxAttempts - Maximum number of attempts to find the element
   * @returns {number} intervalId - ID of the interval used to poll for the element
   */
  function poll(selector, callback, maxAttempts = 10) {
    let attempts = 0;

    const intervalId = setInterval(() => {
      attempts++;
      const element = document.querySelector(selector);

      if (element) {
        clearInterval(intervalId);
        if (callback && typeof callback === 'function') {
          callback(element);
        }
      } else if (attempts >= maxAttempts) {
        clearInterval(intervalId);
        console.log(`Element ${selector} not found after ${maxAttempts} attempts.`);
      }
    }, 1000);

    return intervalId;
  }

  /**
   * Check if an element matches a CSS selector.
   * @param {Element} currentElement - The element to check for a match
   * @param {string} selector - CSS selector to match against
   * @returns {boolean} - True if the selector matches, false otherwise
   */
  function matches(currentElement, selector) {
    while (currentElement !== null && currentElement !== document.body) {
      if (currentElement.matches(selector)) {
        return true;
      }
      currentElement = currentElement.parentElement;
    }

    // 检查 body 元素
    return document.body.matches(selector);
  }

  function icon() {
    return `<?xml version="1.0" encoding="UTF-8"?><svg width="18" height="18" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M18 4H6C4.89543 4 4 4.89543 4 6V18C4 19.1046 4.89543 20 6 20H18C19.1046 20 20 19.1046 20 18V6C20 4.89543 19.1046 4 18 4Z" fill="#2F88FF" stroke="#333" stroke-width="3" stroke-linejoin="round"/><path d="M18 28H6C4.89543 28 4 28.8954 4 30V42C4 43.1046 4.89543 44 6 44H18C19.1046 44 20 43.1046 20 42V30C20 28.8954 19.1046 28 18 28Z" fill="#2F88FF" stroke="#333" stroke-width="3" stroke-linejoin="round"/><path d="M42 4H30C28.8954 4 28 4.89543 28 6V18C28 19.1046 28.8954 20 30 20H42C43.1046 20 44 19.1046 44 18V6C44 4.89543 43.1046 4 42 4Z" fill="#2F88FF" stroke="#333" stroke-width="3" stroke-linejoin="round"/><path d="M28 28H44" stroke="#333" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M36 36H44" stroke="#333" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M28 44H44" stroke="#333" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
  }

  const style = `
  .x-toggle {
    position: fixed;
    top: 18px;
    right: 16px;
    z-index: 99999;
    cursor: pointer;
    opacity: 0.8;
    transition: opacity 0.3s ease-in-out;
  }

  .x-toggle:hover {
    opacity: 1;
  }

  #x-menu-wrapper {
    position: fixed !important;
    top: 55px !important;
    right: 0 !important;
    bottom: 0 !important;
    left: 0 !important;
    z-index: 9999 !important;
    max-width: 100% !important;
    width: 100% !important;
    max-height: calc(100vh - 55px) !important;
    padding: 0 !important;
    background: #fff !important;
    /* border-block-start: 1px solid rgba(5, 5, 5, 0.06) !important; */
  }

  #x-menu-wrapper .sidebar-groups {
    display: grid !important;
    grid-auto-flow: column !important;
    grid-auto-columns: 210px !important;
    max-width: max-content !important;
    gap: 16px !important;
    overflow: auto;
    margin-inline: auto !important;
    border-inline-end: none !important;
  }

  #x-menu-wrapper .doc-content-side {
    display: none !important;
  }

  #x-menu-wrapper .sidebar-group__title {
    font-size: 12px !important;
    margin-block-end: 4px !important;
  }

  #x-menu-wrapper .sidebar-group:nth-child(1) {
    display: none !important;
  }

  #x-menu-wrapper .sidebar-group {
    padding-block-start: 16px !important;
  }

  #x-menu-wrapper .sidebar-group .link {
    padding: 6px 0 !important;
  }

  #x-menu-wrapper .sidebar-group .link-text {
    font-size: 12px !important;
    font-weight: 400 !important;
  }
  `;
  GM_addStyle(style);
})();

QingJ © 2025

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