您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
将 Element Plus 菜单转换为 Dashboard 交互 (按 Shift 键点击可还原为默认菜单)
当前为
// ==UserScript== // @name Hi, Element Plus Component Dashboard🚀 // @namespace https://github.com/xianghongai/Tampermonkey-UserScript // @version 1.0.6 // @description 将 Element Plus 菜单转换为 Dashboard 交互 (按 Shift 键点击可还原为默认菜单) // @author Nicholas Hsiang // @match *://element-plus.org/* // @icon https://avatars.githubusercontent.com/u/68583457 // @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 navSelector = '.navbar-menu'; const menuSelector = '.sidebar'; const groupSelector = '.sidebar-group:not(:first-child)'; const componentItemSelector = '.link'; const titleSelector = '.sidebar-group__title'; let wrapperElement = null; main(); /** * Main function to execute when the script is loaded. */ function main() { ready(() => { poll(navSelector, handler, 500); }); } const wrapperId = 'x-menu-wrapper'; const wrapperClassName = 'x-menu-wrapper'; const toggleClassName = 'x-toggle'; /** * Toggle the target element. */ function handler() { const toggleElement = document.createElement('span'); toggleElement.className = toggleClassName; toggleElement.innerHTML = icon(); toggleElement.addEventListener('click', (event) => { // hold shift key to reset if (event.shiftKey) { wrapperElement.removeAttribute('id'); wrapperElement.style.display = 'block'; return; } // init if (!wrapperElement || wrapperElement.id !== wrapperId) { wrapperElement = setMenuWrapper(); // add event listener to component item componentItemClickEventListener(wrapperElement, componentItemSelector); // handle component page class (hide 'overview' menu item) handleComponentPageClass(wrapperElement); return; } wrapperElement.style.display = wrapperElement.style.display === 'none' ? 'grid' : 'none'; }); document.body.appendChild(toggleElement); // add event listener to navbar navClickEventListener(); } /** * Click the navbar menu element, handle the component page (hide 'overview' menu item). */ function navClickEventListener() { const navElement = document.querySelector(navSelector); if (navElement) { navElement.addEventListener('click', () => { wrapperElement = document.querySelector(menuSelector); setTimeout(() => { if (wrapperElement) { handleComponentPageClass(wrapperElement); } }, 100); }); } } /** * Handle the component page class. * @param {Element} wrapperElement - The wrapper element */ function handleComponentPageClass(wrapperElement) { if (window.location.href.includes('component')) { wrapperElement.classList.add(wrapperClassName); } else { wrapperElement.classList.remove(wrapperClassName); } } /** * Click the component item, hide the menu wrapper. * @param {Element} wrapperElement - The wrapper element * @param {string} componentItemSelector - The selector of the component item */ function componentItemClickEventListener(wrapperElement, componentItemSelector) { wrapperElement.addEventListener('click', (event) => { if (matches(event.target, componentItemSelector)) { wrapperElement.style.display = 'none'; } }); } /** * Set the menu wrapper element. * @returns {Element} - The menu wrapper element */ function setMenuWrapper() { wrapperElement = document.querySelector(menuSelector); wrapperElement.setAttribute('id', wrapperId); // 获取所有 sidebar-group 元素(排除第一个) const groupElements = Array.from(wrapperElement.querySelectorAll(groupSelector)); const componentCounts = []; groupElements.forEach((item) => { const itemSelector = 'a.link'; const itemElements = Array.from(item.querySelectorAll(itemSelector)); const length = itemElements.length; const titleElement = item.querySelector(titleSelector); const title = titleElement.textContent; titleElement.textContent = `${title} (${length})`; componentCounts.push(length); }); const totalCount = componentCounts.reduce((acc, curr) => acc + curr, 0); const totalText = `🚀 共有组件 ${totalCount} 个`; const logoElement = document.querySelector(logoSelector); if (logoElement) { logoElement.title = totalText; } console.log(totalText); return wrapperElement; } /** * 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 = ` .${toggleClassName} { position: fixed; top: 18px; right: 16px; z-index: 99999; cursor: pointer; opacity: 0.8; transition: opacity 0.3s ease-in-out; } .${toggleClassName}:hover { opacity: 1; } #${wrapperId} { 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; */ } #${wrapperId} .sidebar-groups { 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; } #${wrapperId} .doc-content-side { display: none !important; } #${wrapperId} .sidebar-group__title { font-size: 12px !important; margin-block-end: 4px !important; } #${wrapperId}.${wrapperClassName} .sidebar-group:nth-child(1) { display: none !important; } #${wrapperId} .sidebar-group { padding-block-start: 16px !important; } #${wrapperId} .sidebar-group .link { padding: 6px 8px !important; } #${wrapperId} .sidebar-group .link-text { font-size: 12px !important; font-weight: 400 !important; } `; GM_addStyle(style); })();