Greasy Fork is available in English.

ProgressUI-Module

Reusable progress UI module

Script này sẽ không được không được cài đặt trực tiếp. Nó là một thư viện cho các script khác để bao gồm các chỉ thị meta // @require https://update.greatest.deepsurf.us/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. })();