import { useEffect, useRef, useState } from 'react';
type ProgressIndicatorStyle = {
frames: string[];
interval: number;
};
const styles = {
arrow: {
frames: ['↑', '↗', '→', '↘', '↓', '↙', '←', '↖'],
interval: 100,
},
ball_wave: {
frames: ['𓃉𓃉𓃉', '𓃉𓃉∘', '𓃉∘°', '∘°∘', '°∘𓃉', '∘𓃉𓃉'],
interval: 100,
},
blocks1: {
frames: ['░', '▒', '▓', '█'],
interval: 100,
},
blocks2: {
frames: ['▛','▜','▟','▙'],
interval: 100,
},
cym: {
frames: ['⊏', '⊓', '⊐', '⊔'],
interval: 100,
},
dots1: {
frames: ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'],
interval: 50,
},
dots2: {
frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],
interval: 50,
},
dots3: {
frames: ['⠋', '⠙', '⠚', '⠞', '⠖', '⠦', '⠴', '⠲', '⠳', '⠓'],
interval: 50,
},
dots4: {
frames: [
'⠄', '⠆', '⠇', '⠋', '⠙', '⠸', '⠰', '⠠', '⠰', '⠸', '⠙', '⠋', '⠇', '⠆',
],
interval: 50,
},
dots5: {
frames: [
'⠋', '⠙', '⠚', '⠒', '⠂', '⠂', '⠒', '⠲', '⠴', '⠦', '⠖', '⠒', '⠐', '⠐', '⠒', '⠓', '⠋',
],
interval: 50,
},
dots6: {
frames: [
'⠁', '⠉', '⠙', '⠚', '⠒', '⠂', '⠂', '⠒', '⠲', '⠴', '⠤', '⠄', '⠄', '⠤', '⠴', '⠲', '⠒', '⠂', '⠂', '⠒', '⠚', '⠙', '⠉', '⠁',
],
interval: 50,
},
dots7: {
frames: [
'⠈', '⠉', '⠋', '⠓', '⠒', '⠐', '⠐', '⠒', '⠖', '⠦', '⠤', '⠠', '⠠', '⠤', '⠦', '⠖', '⠒', '⠐', '⠐', '⠒', '⠓', '⠋', '⠉', '⠈',
],
interval: 50,
},
dots8: {
frames: [
'⠁', '⠁', '⠉', '⠙', '⠚', '⠒', '⠂', '⠂', '⠒', '⠲', '⠴', '⠤', '⠄', '⠄', '⠤', '⠠', '⠠', '⠤', '⠦', '⠖', '⠒', '⠐', '⠐', '⠒', '⠓', '⠋', '⠉', '⠈', '⠈',
],
interval: 50,
},
dots9: {
frames: ['⢹', '⢺', '⢼', '⣸', '⣇', '⡧', '⡗', '⡏'],
interval: 50,
},
dots10: {
frames: ['⢄', '⢂', '⢁', '⡁', '⡈', '⡐', '⡠'],
interval: 50,
},
dots11: {
frames: ['⠁', '⠂', '⠄', '⡀', '⢀', '⠠', '⠐', '⠈'],
interval: 50,
},
emoji_blink: {
frames: ['😐', '😐', '😐', '😐', '😐', '😐', '😐', '😐', '😐', '😐', '😑'],
interval: 100,
},
emoji_bomb: {
frames: [
'💣 ', ' 💣 ', ' 💣 ', ' 💣', ' 💣', ' 💣', ' 💣', ' 💣', ' 💥', ' ', ' ',
],
interval: 100,
},
emoji_earth: {
frames: ['🌍', '🌎', '🌏'],
interval: 200,
},
emoji_hour: {
frames: [
'🕛', '🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚',
],
interval: 100,
},
emoji_moon: {
frames: ['🌑', '🌒', '🌓', '🌔', '🌕', '🌖', '🌗', '🌘'],
interval: 200,
},
line: {
frames: ['☰', '☱', '☳', '☷', '☶', '☴'],
interval: 100,
},
old: {
frames: ['—', '\\', '|', '/'],
interval: 100,
},
x_plus: {
frames: ['×', '+'],
interval: 100,
},
} satisfies Record<string, ProgressIndicatorStyle>;
const useRafInterval = (callback: () => void, delay: null | number) => {
const savedCallback = useRef(callback);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
if (delay === null) {
return undefined;
}
const id = setInterval(() => savedCallback.current(), delay);
return () => {
clearInterval(id);
};
}, [delay]);
};
export const ProgressIndicator = ({
style,
}: {
style: keyof typeof styles;
}) => {
const { frames, interval } = styles[style];
if (!style) {
throw new Error('Invalid style index');
}
const [index, setIndex] = useState<number>(0);
useRafInterval(() => {
setIndex((index + 1) % frames.length);
}, interval);
return (
<div
style={{
color: '#00d992',
fontFamily: 'monospace',
pointerEvents: 'none',
textAlign: 'center',
userSelect: 'none',
whiteSpace: 'pre',
width: '24px',
}}
>
{frames[index]}
</div>
);
};