- // ==UserScript==
- // @name ProgressUI Module
- // @namespace Violentmonkey Scripts
- // @description Reusable progress UI module
- // @version 0.7
- // @author maanimis
- // @run-at document-idle
- // @license MIT
- // ==/UserScript==
-
- (function () {
- 'use strict';
-
- function _extends() {
- return _extends = Object.assign ? Object.assign.bind() : function (n) {
- for (var e = 1; e < arguments.length; e++) {
- var t = arguments[e];
- for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
- }
- return n;
- }, _extends.apply(null, arguments);
- }
-
- const DEFAULT_THEMES = {
- light: {
- background: '#f8f8f8',
- text: '#333333',
- border: '#e0e0e0',
- progressBg: '#e0e0e0',
- progressFill: '#4CAF50',
- shadow: 'rgba(0,0,0,0.2)'
- },
- dark: {
- background: '#2a2a2a',
- text: '#ffffff',
- border: '#444444',
- progressBg: '#444444',
- progressFill: '#4CAF50',
- shadow: 'rgba(0,0,0,0.5)'
- }
- };
- class ProgressUI {
- constructor(config = {}) {
- this.config = this.validateConfig(config);
- this.elements = this.createUIElements();
- this.applyStyles();
- this.attachToDOM();
- }
- update(message, percent) {
- if (!this.isMounted) return false;
- if (message) {
- this.elements.status.textContent = message;
- }
- if (typeof percent === 'number') {
- const clamped = Math.max(0, Math.min(100, percent));
- this.elements.progressBar.style.width = `${clamped}%`;
- this.elements.percentText.textContent = `${Math.round(clamped)}%`;
- }
- return true;
- }
- scheduleCleanup(delay = 3000, fade = true) {
- window.clearTimeout(this.removalTimeout);
- this.removalTimeout = window.setTimeout(() => this.remove(fade), delay);
- }
- remove(fade = true) {
- if (!this.isMounted) return;
- if (fade) {
- this.elements.container.style.opacity = '0';
- window.setTimeout(() => this.detachFromDOM(), 300);
- } else {
- this.detachFromDOM();
- }
- }
- get isMounted() {
- return document.body.contains(this.elements.container);
- }
- static showQuick(message, config = {}) {
- var _config$closable, _config$duration, _config$percent;
- const instance = new ProgressUI(_extends({}, config, {
- closable: (_config$closable = config.closable) != null ? _config$closable : false,
- duration: (_config$duration = config.duration) != null ? _config$duration : 3000
- }));
- instance.update(message, (_config$percent = config.percent) != null ? _config$percent : 100);
- instance.scheduleCleanup(config.duration, true);
- return instance;
- }
- validateConfig(config) {
- var _config$position, _config$width, _config$theme, _config$title, _config$closable2, _config$colors, _config$duration2;
- return {
- position: (_config$position = config.position) != null ? _config$position : 'top-right',
- width: (_config$width = config.width) != null ? _config$width : '300px',
- theme: (_config$theme = config.theme) != null ? _config$theme : 'light',
- title: (_config$title = config.title) != null ? _config$title : '',
- closable: (_config$closable2 = config.closable) != null ? _config$closable2 : true,
- colors: (_config$colors = config.colors) != null ? _config$colors : {},
- duration: (_config$duration2 = config.duration) != null ? _config$duration2 : 3000
- };
- }
- createUIElements() {
- const container = document.createElement('div');
- const status = document.createElement('div');
- const progressBar = document.createElement('div');
- const percentText = document.createElement('div');
- const progressBarContainer = document.createElement('div');
-
- // Set up the DOM structure
- progressBarContainer.appendChild(progressBar);
- container.appendChild(status);
- container.appendChild(progressBarContainer);
- container.appendChild(percentText);
-
- // Add classes for easier styling (optional)
- container.classList.add('progress-ui-container');
- status.classList.add('progress-ui-status');
- progressBarContainer.classList.add('progress-ui-bar-container');
- progressBar.classList.add('progress-ui-bar');
- percentText.classList.add('progress-ui-percent');
- return {
- container,
- status,
- progressBar,
- percentText
- };
- }
- applyStyles() {
- const colors = this.getThemeColors();
-
- // Container styles
- Object.assign(this.elements.container.style, _extends({
- position: 'fixed',
- zIndex: '9999'
- }, this.getPositionStyles(), {
- backgroundColor: colors.background,
- color: colors.text,
- padding: '15px',
- borderRadius: '5px',
- boxShadow: `0 0 10px ${colors.shadow}`,
- width: this.config.width,
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
- transition: 'opacity 0.3s ease'
- }));
-
- // Status element
- Object.assign(this.elements.status.style, {
- marginBottom: '10px',
- fontSize: '14px',
- fontWeight: '500',
- whiteSpace: 'nowrap',
- overflow: 'hidden',
- textOverflow: 'ellipsis'
- });
-
- // Progress bar container
- Object.assign(this.elements.progressBar.parentElement.style, {
- width: '100%',
- backgroundColor: colors.progressBg,
- borderRadius: '4px',
- height: '10px',
- overflow: 'hidden'
- });
-
- // Progress bar
- Object.assign(this.elements.progressBar.style, {
- height: '100%',
- width: '0%',
- backgroundColor: colors.progressFill,
- transition: 'width 0.3s'
- });
-
- // Percentage text
- Object.assign(this.elements.percentText.style, {
- textAlign: 'right',
- marginTop: '5px',
- fontSize: '12px',
- fontWeight: '600'
- });
- this.addOptionalElements();
- }
- getPositionStyles() {
- const positionStyles = {};
- switch (this.config.position) {
- case 'top-left':
- positionStyles.top = '20px';
- positionStyles.left = '20px';
- break;
- case 'top-center':
- positionStyles.top = '20px';
- positionStyles.left = '50%';
- positionStyles.transform = 'translateX(-50%)';
- break;
- case 'bottom-left':
- positionStyles.bottom = '20px';
- positionStyles.left = '20px';
- break;
- case 'bottom-right':
- positionStyles.bottom = '20px';
- positionStyles.right = '20px';
- break;
- case 'bottom-center':
- positionStyles.bottom = '20px';
- positionStyles.left = '50%';
- positionStyles.transform = 'translateX(-50%)';
- break;
- case 'center':
- positionStyles.top = '50%';
- positionStyles.left = '50%';
- positionStyles.transform = 'translate(-50%, -50%)';
- break;
- default:
- // top-right
- positionStyles.top = '20px';
- positionStyles.right = '20px';
- }
- return positionStyles;
- }
- getThemeColors() {
- const baseTheme = this.config.theme === 'custom' ? DEFAULT_THEMES.light : DEFAULT_THEMES[this.config.theme];
- return _extends({}, baseTheme, this.config.colors);
- }
- addOptionalElements() {
- if (this.config.title) {
- const title = document.createElement('div');
- title.textContent = this.config.title;
- Object.assign(title.style, {
- marginBottom: '10px',
- fontSize: '16px',
- fontWeight: 'bold',
- paddingRight: '15px'
- });
- this.elements.container.prepend(title);
- }
- if (this.config.closable) {
- const closeButton = document.createElement('div');
- closeButton.innerHTML = '×';
- Object.assign(closeButton.style, {
- position: 'absolute',
- top: '5px',
- right: '8px',
- fontSize: '18px',
- fontWeight: 'bold',
- cursor: 'pointer',
- opacity: '0.6'
- });
- closeButton.addEventListener('click', () => this.remove());
- this.elements.container.appendChild(closeButton);
- }
- }
- attachToDOM() {
- document.querySelectorAll('.progress-ui-container').forEach(el => el.remove());
- document.body.appendChild(this.elements.container);
- }
- detachFromDOM() {
- if (this.isMounted) {
- document.body.removeChild(this.elements.container);
- }
- }
- }
- const globalContext = window;
- globalContext.ProgressUI = ProgressUI;
-
- // const globalContext =
- // typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
-
- })();