254 lines
6.0 KiB
JavaScript
254 lines
6.0 KiB
JavaScript
/**
|
|
* Modern Progress Bar Component
|
|
* Enhanced progress bar with true color and Unicode support
|
|
* Requirements: 12.1, 12.2, 12.3
|
|
*/
|
|
|
|
const React = require("react");
|
|
const { Box, Text } = require("ink");
|
|
const useModernTerminal = require("../../hooks/useModernTerminal.js");
|
|
|
|
/**
|
|
* Modern progress bar with enhanced features
|
|
*/
|
|
const ModernProgressBar = ({
|
|
progress = 0,
|
|
total = 100,
|
|
width = 40,
|
|
label = "",
|
|
showPercentage = true,
|
|
showNumbers = false,
|
|
color = "#00FF00",
|
|
backgroundColor = "#333333",
|
|
style = "blocks",
|
|
animated = false,
|
|
...props
|
|
}) => {
|
|
const { colors, unicode, utils, capabilities } = useModernTerminal();
|
|
const [animationFrame, setAnimationFrame] = React.useState(0);
|
|
|
|
// Calculate progress percentage
|
|
const percentage = Math.min(100, Math.max(0, (progress / total) * 100));
|
|
const filled = Math.round((percentage / 100) * width);
|
|
const empty = width - filled;
|
|
|
|
// Animation effect
|
|
React.useEffect(() => {
|
|
if (!animated || !capabilities.enhancedUnicode) return;
|
|
|
|
const interval = setInterval(() => {
|
|
setAnimationFrame((frame) => (frame + 1) % 8);
|
|
}, 150);
|
|
|
|
return () => clearInterval(interval);
|
|
}, [animated, capabilities.enhancedUnicode]);
|
|
|
|
// Generate progress bar content
|
|
const generateProgressBar = () => {
|
|
if (style === "blocks" && capabilities.enhancedUnicode) {
|
|
// Use Unicode block characters
|
|
const fullChar = unicode.getChar("progress", "full", "█");
|
|
const emptyChar = unicode.getChar("progress", "empty", "░");
|
|
|
|
let progressContent = "";
|
|
|
|
if (capabilities.trueColor) {
|
|
// Use true colors
|
|
const fillColor = colors.getInkColor(color);
|
|
const bgColor = colors.getInkColor(backgroundColor);
|
|
|
|
progressContent = (
|
|
<>
|
|
<Text color={fillColor}>{fullChar.repeat(filled)}</Text>
|
|
<Text color={bgColor}>{emptyChar.repeat(empty)}</Text>
|
|
</>
|
|
);
|
|
} else {
|
|
// Fallback to standard colors
|
|
progressContent = (
|
|
<>
|
|
<Text color="green">{fullChar.repeat(filled)}</Text>
|
|
<Text color="gray">{emptyChar.repeat(empty)}</Text>
|
|
</>
|
|
);
|
|
}
|
|
|
|
return progressContent;
|
|
}
|
|
|
|
// ASCII fallback
|
|
const fillChar = "#";
|
|
const emptyChar = "-";
|
|
|
|
return (
|
|
<>
|
|
<Text color="green">{fillChar.repeat(filled)}</Text>
|
|
<Text color="gray">{emptyChar.repeat(empty)}</Text>
|
|
</>
|
|
);
|
|
};
|
|
|
|
// Generate animated spinner if enabled
|
|
const generateSpinner = () => {
|
|
if (!animated) return null;
|
|
|
|
const spinnerChar = utils.createSpinner(animationFrame);
|
|
return (
|
|
<Text color="cyan" marginRight={1}>
|
|
{spinnerChar}
|
|
</Text>
|
|
);
|
|
};
|
|
|
|
// Generate percentage display
|
|
const generatePercentage = () => {
|
|
if (!showPercentage) return null;
|
|
|
|
const percentText = `${Math.round(percentage)}%`;
|
|
return <Text marginLeft={1}>{percentText}</Text>;
|
|
};
|
|
|
|
// Generate numbers display
|
|
const generateNumbers = () => {
|
|
if (!showNumbers) return null;
|
|
|
|
const numbersText = `${progress}/${total}`;
|
|
return (
|
|
<Text color="gray" marginLeft={1}>
|
|
({numbersText})
|
|
</Text>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<Box flexDirection="column" {...props}>
|
|
{label && <Text marginBottom={1}>{label}</Text>}
|
|
|
|
<Box flexDirection="row" alignItems="center">
|
|
{generateSpinner()}
|
|
|
|
<Box flexDirection="row">{generateProgressBar()}</Box>
|
|
|
|
{generatePercentage()}
|
|
{generateNumbers()}
|
|
</Box>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Circular progress indicator using Unicode characters
|
|
*/
|
|
const ModernCircularProgress = ({
|
|
progress = 0,
|
|
total = 100,
|
|
size = "medium",
|
|
color = "#00FF00",
|
|
showPercentage = true,
|
|
...props
|
|
}) => {
|
|
const { colors, unicode, capabilities } = useModernTerminal();
|
|
|
|
const percentage = Math.min(100, Math.max(0, (progress / total) * 100));
|
|
|
|
// Size configurations
|
|
const sizeConfig = {
|
|
small: { radius: 1, chars: ["○", "◐", "◑", "◒", "●"] },
|
|
medium: { radius: 2, chars: ["○", "◔", "◑", "◕", "●"] },
|
|
large: { radius: 3, chars: ["○", "◔", "◑", "◕", "●"] },
|
|
};
|
|
|
|
const config = sizeConfig[size] || sizeConfig.medium;
|
|
const charIndex = Math.floor((percentage / 100) * (config.chars.length - 1));
|
|
const progressChar = config.chars[charIndex];
|
|
|
|
const displayColor = capabilities.trueColor
|
|
? colors.getInkColor(color)
|
|
: "green";
|
|
|
|
return (
|
|
<Box flexDirection="row" alignItems="center" {...props}>
|
|
<Text color={displayColor} fontSize={size === "large" ? 2 : 1}>
|
|
{progressChar}
|
|
</Text>
|
|
|
|
{showPercentage && <Text marginLeft={1}>{Math.round(percentage)}%</Text>}
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Multi-segment progress bar
|
|
*/
|
|
const ModernSegmentedProgress = ({
|
|
segments = [],
|
|
width = 40,
|
|
showLabels = true,
|
|
...props
|
|
}) => {
|
|
const { colors, unicode, capabilities } = useModernTerminal();
|
|
|
|
const total = segments.reduce((sum, segment) => sum + segment.value, 0);
|
|
|
|
const generateSegments = () => {
|
|
let currentPosition = 0;
|
|
|
|
return segments.map((segment, index) => {
|
|
const segmentWidth = Math.round((segment.value / total) * width);
|
|
const char = capabilities.enhancedUnicode
|
|
? unicode.getChar("progress", "full", "█")
|
|
: "#";
|
|
|
|
const segmentColor = capabilities.trueColor
|
|
? colors.getInkColor(segment.color || "#00FF00")
|
|
: segment.color || "green";
|
|
|
|
currentPosition += segmentWidth;
|
|
|
|
return (
|
|
<Text key={index} color={segmentColor}>
|
|
{char.repeat(segmentWidth)}
|
|
</Text>
|
|
);
|
|
});
|
|
};
|
|
|
|
const generateLabels = () => {
|
|
if (!showLabels) return null;
|
|
|
|
return (
|
|
<Box flexDirection="row" marginTop={1} gap={2}>
|
|
{segments.map((segment, index) => (
|
|
<Box key={index} flexDirection="row" alignItems="center">
|
|
<Text
|
|
color={
|
|
capabilities.trueColor
|
|
? colors.getInkColor(segment.color || "#00FF00")
|
|
: segment.color || "green"
|
|
}
|
|
>
|
|
■
|
|
</Text>
|
|
<Text marginLeft={1} color="gray">
|
|
{segment.label} ({segment.value})
|
|
</Text>
|
|
</Box>
|
|
))}
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<Box flexDirection="column" {...props}>
|
|
<Box flexDirection="row">{generateSegments()}</Box>
|
|
{generateLabels()}
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
module.exports = {
|
|
ModernProgressBar,
|
|
ModernCircularProgress,
|
|
ModernSegmentedProgress,
|
|
};
|