SideBarButtonLibrary

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

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/453745/1525524/SideBarButtonLibrary.js

  1. // ==UserScript==
  2. // @name SideBarButtonLibrary
  3. // @namespace
  4. // @version 2.0.0
  5. // @description SideBarButtonLibrary 是一个轻量级的 JavaScript 库,用于创建可展开和收起的侧边栏按钮容器,支持自定义吸附方向(左、右、上、下)、按钮排列方式(水平或垂直)以及样式,适用于快速构建侧边栏工具栏或导航菜单。
  6. // @author otc
  7. // @match *
  8. // @license MIT
  9. // ==/UserScript==
  10.  
  11. const SideBarButtonLibrary = (function() {
  12. // 默认配置
  13. const defaultConfig = {
  14. // 容器默认样式
  15. container: {
  16. width: '5px', // 收起时宽度/高度
  17. expandedWidth: '150px', // 展开时宽度
  18. backgroundColor: 'rgba(0, 0, 0, 0.1)',
  19. transition: 'width 0.3s ease',
  20. className: 'sidebar-button-container', // 默认类名
  21. position: 'left', // 吸附位置:left/right/top/bottom
  22. flexDirection: 'column' // 排列方向:row/column
  23. },
  24. // 按钮默认样式
  25. buttonStyles: {
  26. width: '140px',
  27. background: '#007bff',
  28. color: '#fff',
  29. borderRadius: '4px',
  30. fontSize: '14px',
  31. cursor: 'pointer',
  32. transition: 'background 0.2s ease',
  33. hoverBackground: '#0056b3', // 悬停背景颜色
  34. defaultBackground: '#007bff' // 默认背景颜色
  35. },
  36. // 删除按钮默认样式
  37. deleteButtonStyles: {
  38. right: '-30px', // 收起时位置
  39. expandedRight: '10px', // 展开时位置
  40. background: 'rgba(222, 55, 48, 0.8)',
  41. color: '#fff',
  42. borderRadius: '5px',
  43. cursor: 'pointer',
  44. transition: 'right 0.3s ease',
  45. padding: '5px 10px', // 默认内边距
  46. fontSize: '14px', // 默认字体大小
  47. border: 'none' // 默认边框样式
  48. },
  49. // 动作延迟
  50. hoverDelay: 500, // 悬停后展开延迟
  51. hideDelay: 1000 // 移出后收起延迟
  52. };
  53.  
  54. // 私有函数:获取默认容器样式
  55. function getDefaultContainerStyles(config) {
  56. const baseStyles = {
  57. position: 'fixed',
  58. display: 'flex',
  59. justifyContent: 'center',
  60. alignItems: 'center',
  61. overflow: 'hidden',
  62. zIndex: 10000,
  63. backgroundColor: config.container.backgroundColor,
  64. transition: config.container.transition
  65. };
  66.  
  67. // 根据吸附位置调整样式
  68. switch (config.container.position) {
  69. case 'left':
  70. baseStyles.left = '0';
  71. baseStyles.top = '0';
  72. baseStyles.height = '100%';
  73. baseStyles.width = config.container.width;
  74. break;
  75. case 'right':
  76. baseStyles.right = '0';
  77. baseStyles.top = '0';
  78. baseStyles.height = '100%';
  79. baseStyles.width = config.container.width;
  80. break;
  81. case 'top':
  82. baseStyles.top = '0';
  83. baseStyles.left = '0';
  84. baseStyles.width = '100%';
  85. baseStyles.height = config.container.width;
  86. break;
  87. case 'bottom':
  88. baseStyles.bottom = '0';
  89. baseStyles.left = '0';
  90. baseStyles.width = '100%';
  91. baseStyles.height = config.container.width;
  92. break;
  93. }
  94.  
  95. return baseStyles;
  96. }
  97.  
  98. // 私有函数:创建或获取容器
  99. function getOrCreateButtonContainer(config) {
  100. let container = document.querySelector(`.${config.container.className}`);
  101. if (!container) {
  102. container = document.createElement('div');
  103. container.className = config.container.className;
  104.  
  105. const styles = getDefaultContainerStyles(config);
  106. for (const [key, value] of Object.entries(styles)) {
  107. container.style[key] = value;
  108. }
  109.  
  110. // 设置按钮排列方向
  111. container.style.flexDirection = config.container.flexDirection;
  112.  
  113. // 创建删除按钮
  114. const deleteButton = document.createElement('button');
  115. deleteButton.className = 'delete-button';
  116. deleteButton.innerHTML = '<span class="button-text">×</span>';
  117. deleteButton.style.position = 'absolute';
  118.  
  119. // 根据吸附位置调整删除按钮的位置
  120. switch (config.container.position) {
  121. case 'left':
  122. case 'right':
  123. deleteButton.style.top = '10px';
  124. deleteButton.style.right = config.deleteButtonStyles.right;
  125. break;
  126. case 'top':
  127. deleteButton.style.right = '10px';
  128. deleteButton.style.bottom = config.deleteButtonStyles.expandedRight;
  129. break;
  130. case 'bottom':
  131. deleteButton.style.right = '10px';
  132. deleteButton.style.top = config.deleteButtonStyles.expandedRight;
  133. break;
  134. }
  135.  
  136. // 应用用户自定义的删除按钮样式
  137. const deleteStyles = { ...defaultConfig.deleteButtonStyles, ...config.deleteButtonStyles };
  138. for (const [key, value] of Object.entries(deleteStyles)) {
  139. deleteButton.style[key] = value;
  140. }
  141.  
  142. deleteButton.addEventListener('click', () => {
  143. container.remove();
  144. });
  145.  
  146. container.appendChild(deleteButton);
  147.  
  148. // 鼠标悬停事件
  149. container.addEventListener('mouseenter', () => {
  150. clearTimeout(container.expandTimeout);
  151. clearTimeout(container.hideTimeout);
  152.  
  153. container.expandTimeout = setTimeout(() => {
  154. if (config.container.position === 'left' || config.container.position === 'right') {
  155. container.style.width = config.container.expandedWidth;
  156. } else {
  157. container.style.height = config.container.expandedWidth;
  158. }
  159. deleteButton.style.right = config.deleteButtonStyles.expandedRight;
  160. container.querySelectorAll('.button-text').forEach(text => {
  161. text.style.opacity = 1;
  162. });
  163. }, config.hoverDelay);
  164. });
  165.  
  166. // 鼠标移出事件
  167. container.addEventListener('mouseleave', () => {
  168. clearTimeout(container.expandTimeout);
  169.  
  170. container.hideTimeout = setTimeout(() => {
  171. if (config.container.position === 'left' || config.container.position === 'right') {
  172. container.style.width = config.container.width;
  173. } else {
  174. container.style.height = config.container.width;
  175. }
  176. deleteButton.style.right = config.deleteButtonStyles.right;
  177. container.querySelectorAll('.button-text').forEach(text => {
  178. text.style.opacity = 0;
  179. });
  180. }, config.hideDelay);
  181. });
  182.  
  183. document.body.appendChild(container);
  184. }
  185. return container;
  186. }
  187.  
  188. // 公开接口:创建按钮
  189. function createButtons(buttons, config = {}) {
  190. const finalConfig = { ...defaultConfig, ...config };
  191. const container = getOrCreateButtonContainer(finalConfig);
  192.  
  193. buttons.forEach((button, index) => {
  194. const buttonElement = document.createElement('button');
  195. buttonElement.innerHTML = `<span class="button-text">${button.name}</span>`;
  196. buttonElement.style.margin = '2px 0';
  197. buttonElement.style.width = finalConfig.buttonStyles.width;
  198. buttonElement.style.border = 'none';
  199. buttonElement.style.background = finalConfig.buttonStyles.defaultBackground;
  200. buttonElement.style.color = finalConfig.buttonStyles.color;
  201. buttonElement.style.borderRadius = finalConfig.buttonStyles.borderRadius;
  202. buttonElement.style.fontSize = finalConfig.buttonStyles.fontSize;
  203. buttonElement.style.cursor = finalConfig.buttonStyles.cursor;
  204. buttonElement.style.transition = finalConfig.buttonStyles.transition;
  205.  
  206. const buttonText = buttonElement.querySelector('.button-text');
  207. buttonText.style.opacity = 0;
  208. buttonText.style.transition = 'opacity 0.3s ease';
  209.  
  210. // 使用配置中的悬停和默认样式
  211. buttonElement.addEventListener('mouseover', () => {
  212. buttonElement.style.background = finalConfig.buttonStyles.hoverBackground;
  213. });
  214. buttonElement.addEventListener('mouseout', () => {
  215. buttonElement.style.background = finalConfig.buttonStyles.defaultBackground;
  216. });
  217.  
  218. buttonElement.addEventListener('click', button.func);
  219.  
  220. container.appendChild(buttonElement);
  221. });
  222. }
  223.  
  224. // 暴露公共接口
  225. return {
  226. createButtons: createButtons
  227. };
  228. })();
  229.  
  230. // 使用示例
  231. // SideBarButtonLibrary.createButtons([
  232. // { name: 'Button 1', func: () => alert('Button 1 clicked') },
  233. // { name: 'Button 2', func: () => alert('Button 2 clicked') }
  234. // ], {
  235. // container: {
  236. // width: '5px', // 自定义收起宽度/高度
  237. // expandedWidth: '200px', // 自定义展开宽度
  238. // backgroundColor: 'rgba(0, 0, 0, 0.2)', // 自定义背景颜色
  239. // className: 'my-custom-sidebar-container', // 自定义容器类名
  240. // position: 'top', // 吸附到顶部
  241. // flexDirection: 'row' // 按钮水平排列
  242. // },
  243. // buttonStyles: {
  244. // width: '180px', // 自定义按钮宽度
  245. // background: '#ff5722', // 自定义按钮默认背景
  246. // hoverBackground: '#cc4400', // 自定义按钮悬停背景
  247. // defaultBackground: '#ff5722' // 自定义按钮默认背景
  248. // },
  249. // deleteButtonStyles: {
  250. // right: '-40px', // 自定义收起时删除按钮位置
  251. // expandedRight: '20px', // 自定义展开时删除按钮位置
  252. // background: 'rgba(255, 0, 0, 0.8)', // 自定义删除按钮背景颜色
  253. // color: '#000' // 自定义删除按钮字体颜色
  254. // },
  255. // hoverDelay: 300, // 自定义悬停延迟
  256. // hideDelay: 800 // 自定义隐藏延迟
  257. // });