SideBarButtonLibrary

SideBarButtonLibrary 是一个轻量级的 JavaScript 库,用于创建可展开和收起的侧边栏按钮容器,支持自定义吸附方向(左、右、上、下)、按钮排列方式(水平或垂直)以及样式,适用于快速构建侧边栏工具栏或导航菜单。

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.cn-greasyfork.org/scripts/453745/1525524/SideBarButtonLibrary.js

// ==UserScript==
// @name         SideBarButtonLibrary
// @namespace    
// @version      2.0.0
// @description  SideBarButtonLibrary 是一个轻量级的 JavaScript 库,用于创建可展开和收起的侧边栏按钮容器,支持自定义吸附方向(左、右、上、下)、按钮排列方式(水平或垂直)以及样式,适用于快速构建侧边栏工具栏或导航菜单。
// @author       otc
// @match        *
// @license MIT
// ==/UserScript==

const SideBarButtonLibrary = (function() {
    // 默认配置
    const defaultConfig = {
        // 容器默认样式
        container: {
            width: '5px',               // 收起时宽度/高度
            expandedWidth: '150px',     // 展开时宽度
            backgroundColor: 'rgba(0, 0, 0, 0.1)',
            transition: 'width 0.3s ease',
            className: 'sidebar-button-container', // 默认类名
            position: 'left',           // 吸附位置:left/right/top/bottom
            flexDirection: 'column'     // 排列方向:row/column
        },
        // 按钮默认样式
        buttonStyles: {
            width: '140px',
            background: '#007bff',
            color: '#fff',
            borderRadius: '4px',
            fontSize: '14px',
            cursor: 'pointer',
            transition: 'background 0.2s ease',
            hoverBackground: '#0056b3', // 悬停背景颜色
            defaultBackground: '#007bff' // 默认背景颜色
        },
        // 删除按钮默认样式
        deleteButtonStyles: {
            right: '-30px',             // 收起时位置
            expandedRight: '10px',      // 展开时位置
            background: 'rgba(222, 55, 48, 0.8)',
            color: '#fff',
            borderRadius: '5px',
            cursor: 'pointer',
            transition: 'right 0.3s ease',
            padding: '5px 10px',        // 默认内边距
            fontSize: '14px',          // 默认字体大小
            border: 'none'             // 默认边框样式
        },
        // 动作延迟
        hoverDelay: 500,   // 悬停后展开延迟
        hideDelay: 1000    // 移出后收起延迟
    };

    // 私有函数:获取默认容器样式
    function getDefaultContainerStyles(config) {
        const baseStyles = {
            position: 'fixed',
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            overflow: 'hidden',
            zIndex: 10000,
            backgroundColor: config.container.backgroundColor,
            transition: config.container.transition
        };

        // 根据吸附位置调整样式
        switch (config.container.position) {
            case 'left':
                baseStyles.left = '0';
                baseStyles.top = '0';
                baseStyles.height = '100%';
                baseStyles.width = config.container.width;
                break;
            case 'right':
                baseStyles.right = '0';
                baseStyles.top = '0';
                baseStyles.height = '100%';
                baseStyles.width = config.container.width;
                break;
            case 'top':
                baseStyles.top = '0';
                baseStyles.left = '0';
                baseStyles.width = '100%';
                baseStyles.height = config.container.width;
                break;
            case 'bottom':
                baseStyles.bottom = '0';
                baseStyles.left = '0';
                baseStyles.width = '100%';
                baseStyles.height = config.container.width;
                break;
        }

        return baseStyles;
    }

    // 私有函数:创建或获取容器
    function getOrCreateButtonContainer(config) {
        let container = document.querySelector(`.${config.container.className}`);
        if (!container) {
            container = document.createElement('div');
            container.className = config.container.className;

            const styles = getDefaultContainerStyles(config);
            for (const [key, value] of Object.entries(styles)) {
                container.style[key] = value;
            }

            // 设置按钮排列方向
            container.style.flexDirection = config.container.flexDirection;

            // 创建删除按钮
            const deleteButton = document.createElement('button');
            deleteButton.className = 'delete-button';
            deleteButton.innerHTML = '<span class="button-text">×</span>';
            deleteButton.style.position = 'absolute';

            // 根据吸附位置调整删除按钮的位置
            switch (config.container.position) {
                case 'left':
                case 'right':
                    deleteButton.style.top = '10px';
                    deleteButton.style.right = config.deleteButtonStyles.right;
                    break;
                case 'top':
                    deleteButton.style.right = '10px';
                    deleteButton.style.bottom = config.deleteButtonStyles.expandedRight;
                    break;
                case 'bottom':
                    deleteButton.style.right = '10px';
                    deleteButton.style.top = config.deleteButtonStyles.expandedRight;
                    break;
            }

            // 应用用户自定义的删除按钮样式
            const deleteStyles = { ...defaultConfig.deleteButtonStyles, ...config.deleteButtonStyles };
            for (const [key, value] of Object.entries(deleteStyles)) {
                deleteButton.style[key] = value;
            }

            deleteButton.addEventListener('click', () => {
                container.remove();
            });

            container.appendChild(deleteButton);

            // 鼠标悬停事件
            container.addEventListener('mouseenter', () => {
                clearTimeout(container.expandTimeout);
                clearTimeout(container.hideTimeout);

                container.expandTimeout = setTimeout(() => {
                    if (config.container.position === 'left' || config.container.position === 'right') {
                        container.style.width = config.container.expandedWidth;
                    } else {
                        container.style.height = config.container.expandedWidth;
                    }
                    deleteButton.style.right = config.deleteButtonStyles.expandedRight;
                    container.querySelectorAll('.button-text').forEach(text => {
                        text.style.opacity = 1;
                    });
                }, config.hoverDelay);
            });

            // 鼠标移出事件
            container.addEventListener('mouseleave', () => {
                clearTimeout(container.expandTimeout);

                container.hideTimeout = setTimeout(() => {
                    if (config.container.position === 'left' || config.container.position === 'right') {
                        container.style.width = config.container.width;
                    } else {
                        container.style.height = config.container.width;
                    }
                    deleteButton.style.right = config.deleteButtonStyles.right;
                    container.querySelectorAll('.button-text').forEach(text => {
                        text.style.opacity = 0;
                    });
                }, config.hideDelay);
            });

            document.body.appendChild(container);
        }
        return container;
    }

    // 公开接口:创建按钮
    function createButtons(buttons, config = {}) {
        const finalConfig = { ...defaultConfig, ...config };
        const container = getOrCreateButtonContainer(finalConfig);

        buttons.forEach((button, index) => {
            const buttonElement = document.createElement('button');
            buttonElement.innerHTML = `<span class="button-text">${button.name}</span>`;
            buttonElement.style.margin = '2px 0';
            buttonElement.style.width = finalConfig.buttonStyles.width;
            buttonElement.style.border = 'none';
            buttonElement.style.background = finalConfig.buttonStyles.defaultBackground;
            buttonElement.style.color = finalConfig.buttonStyles.color;
            buttonElement.style.borderRadius = finalConfig.buttonStyles.borderRadius;
            buttonElement.style.fontSize = finalConfig.buttonStyles.fontSize;
            buttonElement.style.cursor = finalConfig.buttonStyles.cursor;
            buttonElement.style.transition = finalConfig.buttonStyles.transition;

            const buttonText = buttonElement.querySelector('.button-text');
            buttonText.style.opacity = 0;
            buttonText.style.transition = 'opacity 0.3s ease';

            // 使用配置中的悬停和默认样式
            buttonElement.addEventListener('mouseover', () => {
                buttonElement.style.background = finalConfig.buttonStyles.hoverBackground;
            });
            buttonElement.addEventListener('mouseout', () => {
                buttonElement.style.background = finalConfig.buttonStyles.defaultBackground;
            });

            buttonElement.addEventListener('click', button.func);

            container.appendChild(buttonElement);
        });
    }

    // 暴露公共接口
    return {
        createButtons: createButtons
    };
})();

// 使用示例
// SideBarButtonLibrary.createButtons([
//     { name: 'Button 1', func: () => alert('Button 1 clicked') },
//     { name: 'Button 2', func: () => alert('Button 2 clicked') }
// ], {
//     container: {
//         width: '5px',               // 自定义收起宽度/高度
//         expandedWidth: '200px',     // 自定义展开宽度
//         backgroundColor: 'rgba(0, 0, 0, 0.2)', // 自定义背景颜色
//         className: 'my-custom-sidebar-container', // 自定义容器类名
//         position: 'top',            // 吸附到顶部
//         flexDirection: 'row'        // 按钮水平排列
//     },
//     buttonStyles: {
//         width: '180px',             // 自定义按钮宽度
//         background: '#ff5722',      // 自定义按钮默认背景
//         hoverBackground: '#cc4400', // 自定义按钮悬停背景
//         defaultBackground: '#ff5722' // 自定义按钮默认背景
//     },
//     deleteButtonStyles: {
//         right: '-40px',             // 自定义收起时删除按钮位置
//         expandedRight: '20px',      // 自定义展开时删除按钮位置
//         background: 'rgba(255, 0, 0, 0.8)', // 自定义删除按钮背景颜色
//         color: '#000'               // 自定义删除按钮字体颜色
//     },
//     hoverDelay: 300,                // 自定义悬停延迟
//     hideDelay: 800                  // 自定义隐藏延迟
// });