UNPKG

vanta-auditor-tui

Version:

Beautiful terminal UI for exporting Vanta audit evidence with ZIP support and progress tracking

75 lines 3.82 kB
import React, { useState, useEffect } from 'react'; import { Box, Text } from 'ink'; import { theme } from '../theme.js'; export function AnimatedProgressBar({ value, label, width = 30, showPercentage = true, animationSpeed = 20 }) { const [displayValue, setDisplayValue] = useState(0); const [springVelocity, setSpringVelocity] = useState(0); useEffect(() => { const targetValue = Math.max(0, Math.min(100, value)); let currentValue = displayValue; let velocity = springVelocity; const interval = setInterval(() => { // Spring physics simulation const stiffness = 0.15; const damping = 0.85; const force = (targetValue - currentValue) * stiffness; velocity = velocity * damping + force; currentValue += velocity; // Check if we're close enough to the target if (Math.abs(targetValue - currentValue) < 0.1 && Math.abs(velocity) < 0.1) { currentValue = targetValue; clearInterval(interval); } setDisplayValue(currentValue); setSpringVelocity(velocity); }, animationSpeed); return () => clearInterval(interval); }, [value]); const filledWidth = Math.round((displayValue / 100) * width); const emptyWidth = width - filledWidth; // Create gradient effect with multiple characters const getBarCharacter = (position, filled, total) => { const relativePos = position / total; const fillRatio = filled / total; if (relativePos < fillRatio - 0.1) return '█'; if (relativePos < fillRatio - 0.05) return '▓'; if (relativePos < fillRatio) return '▒'; return '░'; }; const barContent = Array.from({ length: width }, (_, i) => getBarCharacter(i, filledWidth, width)).join(''); // Pulsing effect for active downloads const pulseIntensity = Math.sin(Date.now() / 200) * 0.5 + 0.5; const barColor = displayValue < 100 ? pulseIntensity > 0.5 ? theme.colors.primary : theme.colors.primaryLight : theme.colors.success; return (React.createElement(Box, { flexDirection: "column" }, label && (React.createElement(Box, { marginBottom: 0 }, React.createElement(Text, { color: theme.colors.text }, label))), React.createElement(Box, null, React.createElement(Text, { color: theme.colors.dim }, "["), React.createElement(Text, { color: barColor }, barContent), React.createElement(Text, { color: theme.colors.dim }, "]"), showPercentage && (React.createElement(Box, { marginLeft: 1 }, React.createElement(Text, { color: displayValue === 100 ? theme.colors.success : theme.colors.text }, Math.round(displayValue), "%")))))); } export function MultiLevelProgress({ overall, current = 0, label, stats }) { return (React.createElement(Box, { flexDirection: "column", paddingY: 1 }, React.createElement(AnimatedProgressBar, { value: overall, label: "Overall Progress", width: 40 }), current > 0 && (React.createElement(Box, { marginTop: 1 }, React.createElement(AnimatedProgressBar, { value: current, label: label || "Current File", width: 40 }))), stats && (React.createElement(Box, { marginTop: 1, flexDirection: "column" }, React.createElement(Text, { color: theme.colors.dim }, "\uD83D\uDCCA ", stats.downloaded, "/", stats.total, " files", stats.speed && ` • ${stats.speed}`, stats.eta && ` • ETA: ${stats.eta}`))))); } //# sourceMappingURL=AnimatedProgressBar.js.map