ProgressUI-Module

Reusable progress UI module

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

  1. // ==UserScript==
  2. // @name ProgressUI Module
  3. // @namespace Violentmonkey Scripts
  4. // @description Reusable progress UI module
  5. // @version 0.7
  6. // @author maanimis
  7. // @run-at document-idle
  8. // @license MIT
  9. // ==/UserScript==
  10.  
  11. (function () {
  12. 'use strict';
  13.  
  14. function _extends() {
  15. return _extends = Object.assign ? Object.assign.bind() : function (n) {
  16. for (var e = 1; e < arguments.length; e++) {
  17. var t = arguments[e];
  18. for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
  19. }
  20. return n;
  21. }, _extends.apply(null, arguments);
  22. }
  23.  
  24. const DEFAULT_THEMES = {
  25. light: {
  26. background: '#f8f8f8',
  27. text: '#333333',
  28. border: '#e0e0e0',
  29. progressBg: '#e0e0e0',
  30. progressFill: '#4CAF50',
  31. shadow: 'rgba(0,0,0,0.2)'
  32. },
  33. dark: {
  34. background: '#2a2a2a',
  35. text: '#ffffff',
  36. border: '#444444',
  37. progressBg: '#444444',
  38. progressFill: '#4CAF50',
  39. shadow: 'rgba(0,0,0,0.5)'
  40. }
  41. };
  42. class ProgressUI {
  43. constructor(config = {}) {
  44. this.config = this.validateConfig(config);
  45. this.elements = this.createUIElements();
  46. this.applyStyles();
  47. this.attachToDOM();
  48. }
  49. update(message, percent) {
  50. if (!this.isMounted) return false;
  51. if (message) {
  52. this.elements.status.textContent = message;
  53. }
  54. if (typeof percent === 'number') {
  55. const clamped = Math.max(0, Math.min(100, percent));
  56. this.elements.progressBar.style.width = `${clamped}%`;
  57. this.elements.percentText.textContent = `${Math.round(clamped)}%`;
  58. }
  59. return true;
  60. }
  61. scheduleCleanup(delay = 3000, fade = true) {
  62. window.clearTimeout(this.removalTimeout);
  63. this.removalTimeout = window.setTimeout(() => this.remove(fade), delay);
  64. }
  65. remove(fade = true) {
  66. if (!this.isMounted) return;
  67. if (fade) {
  68. this.elements.container.style.opacity = '0';
  69. window.setTimeout(() => this.detachFromDOM(), 300);
  70. } else {
  71. this.detachFromDOM();
  72. }
  73. }
  74. get isMounted() {
  75. return document.body.contains(this.elements.container);
  76. }
  77. static showQuick(message, config = {}) {
  78. var _config$closable, _config$duration, _config$percent;
  79. const instance = new ProgressUI(_extends({}, config, {
  80. closable: (_config$closable = config.closable) != null ? _config$closable : false,
  81. duration: (_config$duration = config.duration) != null ? _config$duration : 3000
  82. }));
  83. instance.update(message, (_config$percent = config.percent) != null ? _config$percent : 100);
  84. instance.scheduleCleanup(config.duration, true);
  85. return instance;
  86. }
  87. validateConfig(config) {
  88. var _config$position, _config$width, _config$theme, _config$title, _config$closable2, _config$colors, _config$duration2;
  89. return {
  90. position: (_config$position = config.position) != null ? _config$position : 'top-right',
  91. width: (_config$width = config.width) != null ? _config$width : '300px',
  92. theme: (_config$theme = config.theme) != null ? _config$theme : 'light',
  93. title: (_config$title = config.title) != null ? _config$title : '',
  94. closable: (_config$closable2 = config.closable) != null ? _config$closable2 : true,
  95. colors: (_config$colors = config.colors) != null ? _config$colors : {},
  96. duration: (_config$duration2 = config.duration) != null ? _config$duration2 : 3000
  97. };
  98. }
  99. createUIElements() {
  100. const container = document.createElement('div');
  101. const status = document.createElement('div');
  102. const progressBar = document.createElement('div');
  103. const percentText = document.createElement('div');
  104. const progressBarContainer = document.createElement('div');
  105.  
  106. // Set up the DOM structure
  107. progressBarContainer.appendChild(progressBar);
  108. container.appendChild(status);
  109. container.appendChild(progressBarContainer);
  110. container.appendChild(percentText);
  111.  
  112. // Add classes for easier styling (optional)
  113. container.classList.add('progress-ui-container');
  114. status.classList.add('progress-ui-status');
  115. progressBarContainer.classList.add('progress-ui-bar-container');
  116. progressBar.classList.add('progress-ui-bar');
  117. percentText.classList.add('progress-ui-percent');
  118. return {
  119. container,
  120. status,
  121. progressBar,
  122. percentText
  123. };
  124. }
  125. applyStyles() {
  126. const colors = this.getThemeColors();
  127.  
  128. // Container styles
  129. Object.assign(this.elements.container.style, _extends({
  130. position: 'fixed',
  131. zIndex: '9999'
  132. }, this.getPositionStyles(), {
  133. backgroundColor: colors.background,
  134. color: colors.text,
  135. padding: '15px',
  136. borderRadius: '5px',
  137. boxShadow: `0 0 10px ${colors.shadow}`,
  138. width: this.config.width,
  139. fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
  140. transition: 'opacity 0.3s ease'
  141. }));
  142.  
  143. // Status element
  144. Object.assign(this.elements.status.style, {
  145. marginBottom: '10px',
  146. fontSize: '14px',
  147. fontWeight: '500',
  148. whiteSpace: 'nowrap',
  149. overflow: 'hidden',
  150. textOverflow: 'ellipsis'
  151. });
  152.  
  153. // Progress bar container
  154. Object.assign(this.elements.progressBar.parentElement.style, {
  155. width: '100%',
  156. backgroundColor: colors.progressBg,
  157. borderRadius: '4px',
  158. height: '10px',
  159. overflow: 'hidden'
  160. });
  161.  
  162. // Progress bar
  163. Object.assign(this.elements.progressBar.style, {
  164. height: '100%',
  165. width: '0%',
  166. backgroundColor: colors.progressFill,
  167. transition: 'width 0.3s'
  168. });
  169.  
  170. // Percentage text
  171. Object.assign(this.elements.percentText.style, {
  172. textAlign: 'right',
  173. marginTop: '5px',
  174. fontSize: '12px',
  175. fontWeight: '600'
  176. });
  177. this.addOptionalElements();
  178. }
  179. getPositionStyles() {
  180. const positionStyles = {};
  181. switch (this.config.position) {
  182. case 'top-left':
  183. positionStyles.top = '20px';
  184. positionStyles.left = '20px';
  185. break;
  186. case 'top-center':
  187. positionStyles.top = '20px';
  188. positionStyles.left = '50%';
  189. positionStyles.transform = 'translateX(-50%)';
  190. break;
  191. case 'bottom-left':
  192. positionStyles.bottom = '20px';
  193. positionStyles.left = '20px';
  194. break;
  195. case 'bottom-right':
  196. positionStyles.bottom = '20px';
  197. positionStyles.right = '20px';
  198. break;
  199. case 'bottom-center':
  200. positionStyles.bottom = '20px';
  201. positionStyles.left = '50%';
  202. positionStyles.transform = 'translateX(-50%)';
  203. break;
  204. case 'center':
  205. positionStyles.top = '50%';
  206. positionStyles.left = '50%';
  207. positionStyles.transform = 'translate(-50%, -50%)';
  208. break;
  209. default:
  210. // top-right
  211. positionStyles.top = '20px';
  212. positionStyles.right = '20px';
  213. }
  214. return positionStyles;
  215. }
  216. getThemeColors() {
  217. const baseTheme = this.config.theme === 'custom' ? DEFAULT_THEMES.light : DEFAULT_THEMES[this.config.theme];
  218. return _extends({}, baseTheme, this.config.colors);
  219. }
  220. addOptionalElements() {
  221. if (this.config.title) {
  222. const title = document.createElement('div');
  223. title.textContent = this.config.title;
  224. Object.assign(title.style, {
  225. marginBottom: '10px',
  226. fontSize: '16px',
  227. fontWeight: 'bold',
  228. paddingRight: '15px'
  229. });
  230. this.elements.container.prepend(title);
  231. }
  232. if (this.config.closable) {
  233. const closeButton = document.createElement('div');
  234. closeButton.innerHTML = '×';
  235. Object.assign(closeButton.style, {
  236. position: 'absolute',
  237. top: '5px',
  238. right: '8px',
  239. fontSize: '18px',
  240. fontWeight: 'bold',
  241. cursor: 'pointer',
  242. opacity: '0.6'
  243. });
  244. closeButton.addEventListener('click', () => this.remove());
  245. this.elements.container.appendChild(closeButton);
  246. }
  247. }
  248. attachToDOM() {
  249. document.querySelectorAll('.progress-ui-container').forEach(el => el.remove());
  250. document.body.appendChild(this.elements.container);
  251. }
  252. detachFromDOM() {
  253. if (this.isMounted) {
  254. document.body.removeChild(this.elements.container);
  255. }
  256. }
  257. }
  258. const globalContext = window;
  259. globalContext.ProgressUI = ProgressUI;
  260.  
  261. // const globalContext =
  262. // typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
  263.  
  264. })();

QingJ © 2025

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