/**
* WikiShieldProgressBar - React/Preact component for progress indicators
* Provides both a React component and an imperative API for backward compatibility
*/
import { h, Component } from 'preact';
import { render } from 'preact';
/**
* Progress Bar Component
*/
class ProgressBarComponent extends Component {
constructor(props) {
super(props);
this.state = {
text: '',
width: 0,
color: 'var(--main-blue)',
opacity: 1,
isVisible: true
};
}
componentDidMount() {
// Notify parent that component is ready
if (this.props.onMount) {
this.props.onMount(this);
}
}
/**
* Update the progress bar
* @param {String} text The text to display
* @param {Number} width Percentage (0-1)
* @param {String} color CSS color for the bar
*/
updateProgress(text, width, color) {
this.setState({
text,
width: Math.round(width * 100),
color,
opacity: 1,
isVisible: true
});
// Auto-remove when complete
if (width === 1) {
this.scheduleRemoval(2000);
}
}
/**
* Schedule removal of the progress bar
* @param {Number} time Time in ms before removal
*/
scheduleRemoval(time) {
// Fade out
setTimeout(() => {
this.setState({ opacity: 0 });
}, time - 300);
// Remove
setTimeout(() => {
this.setState({ isVisible: false });
if (this.props.onRemove) {
this.props.onRemove();
}
}, time);
}
render() {
const { text, width, color, opacity, isVisible } = this.state;
if (!isVisible) {
return null;
}
return (
<div
className="progress-bar"
style={{ opacity }}
>
<div
className="progress-bar-overlay"
style={{ width: `${width}%` }}
/>
<div className="progress-bar-text">
{text}
</div>
</div>
);
}
}
/**
* Imperative API wrapper for WikiShieldProgressBar
* Maintains backward compatibility with the original class-based API
*/
export class WikiShieldProgressBar {
constructor() {
this.nextChangeTime = 0;
this.updateQueue = [];
this.componentRef = null;
// Find or create container
this.container = document.querySelector('#progress-bar-container');
if (!this.container) {
console.warn('Progress bar container not found');
return;
}
// Create a wrapper div for this progress bar instance
this.element = document.createElement('div');
this.container.appendChild(this.element);
// Render the React component
render(
<ProgressBarComponent
onMount={(ref) => {
this.componentRef = ref;
this.processQueue();
}}
onRemove={() => {
this.cleanup();
}}
/>,
this.element
);
}
/**
* Set the progress bar
* @param {String} text The text to display
* @param {Number} width Percentage (0-1)
* @param {String} color CSS color for the bar
*/
set(text, width, color) {
const delay = Math.max(0, this.nextChangeTime - Date.now());
this.updateQueue.push({
text,
width,
color,
delay
});
this.nextChangeTime = Math.max(Date.now() + 200, this.nextChangeTime + 200);
// Process queue if component is ready
if (this.componentRef) {
this.processQueue();
}
}
/**
* Process queued updates
*/
processQueue() {
while (this.updateQueue.length > 0 && this.componentRef) {
const update = this.updateQueue.shift();
setTimeout(() => {
if (this.componentRef) {
this.componentRef.updateProgress(update.text, update.width, update.color);
}
}, update.delay);
}
}
/**
* Remove the progress bar after a given time
* @param {Number} time The time to wait before removing the progress bar
*/
remove(time) {
if (this.componentRef) {
this.componentRef.scheduleRemoval(time);
}
}
/**
* Cleanup when removing
*/
cleanup() {
if (this.element && this.element.parentNode) {
// Unmount React component and remove element
render(null, this.element);
this.element.parentNode.removeChild(this.element);
}
this.componentRef = null;
this.updateQueue = [];
}
}
/**
* React Hook for using progress bars in functional components
* Example usage:
*
* const MyComponent = () => {
* const [progress, setProgress] = useProgressBar();
*
* const doWork = async () => {
* setProgress('Working...', 0.5, 'blue');
* await someAsyncWork();
* setProgress('Done!', 1.0, 'green');
* };
*
* return <button onClick={doWork}>Start</button>;
* };
*/
export function useProgressBar() {
const progressBarRef = { current: null };
const setProgress = (text, width, color = 'var(--main-blue)') => {
if (!progressBarRef.current) {
progressBarRef.current = new WikiShieldProgressBar();
}
progressBarRef.current.set(text, width, color);
};
return [progressBarRef, setProgress];
}
/**
* Functional component version for direct use in JSX
* Example:
*
* <ProgressBar text="Loading..." width={0.5} color="blue" />
*/
export const ProgressBar = ({ text, width, color = 'var(--main-blue)', onComplete }) => {
return (
<div className="progress-bar">
<div
className="progress-bar-overlay"
style={{
width: `${Math.round(width * 100)}%`,
background: color,
transition: 'width 0.2s ease'
}}
/>
<div className="progress-bar-text">
{text}
</div>
</div>
);
};