Hi, Ant Design Component Dashboard (5.x)🚀

Hi, Ant Design Component Dashboard (5.x)

目前為 2025-03-26 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Hi, Ant Design Component Dashboard (5.x)🚀
// @namespace    https://github.com/xianghongai/Tampermonkey-UserScript
// @version      1.0.0
// @description  Hi, Ant Design Component Dashboard (5.x)
// @author       Nicholas Hsiang
// @match        *://ant.design/components/*
// @icon         https://gw.alipayobjects.com/zos/rmsportal/rlpTLlbMzTNYuZGGCVYM.png
// @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 = 'img[alt="logo"]';
  const menuSelector = '.ant-app>main>.ant-col:nth-child(1)';
  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', () => {
      if (!wrapperEl || wrapperEl.id !== 'x-menu-wrapper') {
        wrapperEl = dashboard();
        click(wrapperEl, '.ant-menu-item');
        return;
      }
      wrapperEl.style.display = wrapperEl.style.display === 'none' ? 'block' : '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.id = 'x-menu-wrapper';

    const groupSelector = '.ant-menu>.ant-menu-item-group';
    const groupEl = Array.from(wrapperEl.querySelectorAll(groupSelector));

    const lengths = [];

    groupEl.forEach((item) => {
      const itemSelector = '.ant-menu-item-group-list>.ant-menu-item';
      const itemEl = Array.from(item.querySelectorAll(itemSelector));
      const length = itemEl.length;
      const titleSelector = '.ant-menu-item-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);
    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: 24px;
    right: 680px;
    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: 64px !important;
    right: 0 !important;
    bottom: 0 !important;
    left: 0 !important;
    z-index: 9999 !important;
    max-width: 100% !important;
    max-height: calc(100vh - 64px) !important;
    padding: 16px !important;
    background: #fff !important;
    border-block-start: 1px solid rgba(5, 5, 5, 0.06) !important;
  }

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

  #x-menu-wrapper .ant-menu > .ant-menu-item-only-child {
    display: none !important;
  }

  #x-menu-wrapper .ant-menu > .ant-menu-item-group {
    display: grid !important;
    grid-template-rows: auto 1fr;
    overflow: hidden !important;
  }

  #x-menu-wrapper .ant-menu > .ant-menu-item-group > .ant-menu-item-group-list {
    overflow: auto !important;
  }

  #x-menu-wrapper .ant-menu > .ant-menu-item-group > .ant-menu-item-group-list > .ant-menu-item {
    height: 28px !important;
    line-height: 28px !important;
  }

  `;
  GM_addStyle(style);
})();

QingJ © 2025

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