Just a whole lot of crap
This commit is contained in:
410
src/tui/components/common/ModernStatusIndicator.jsx
Normal file
410
src/tui/components/common/ModernStatusIndicator.jsx
Normal file
@@ -0,0 +1,410 @@
|
||||
/**
|
||||
* Modern Status Indicator Component
|
||||
* Enhanced status indicators 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 status indicator with enhanced visuals
|
||||
*/
|
||||
const ModernStatusIndicator = ({
|
||||
status = "idle",
|
||||
label = "",
|
||||
showLabel = true,
|
||||
size = "medium",
|
||||
animated = false,
|
||||
customColor,
|
||||
customIcon,
|
||||
...props
|
||||
}) => {
|
||||
const { colors, unicode, utils, capabilities } = useModernTerminal();
|
||||
const [animationFrame, setAnimationFrame] = React.useState(0);
|
||||
|
||||
// Status configurations
|
||||
const statusConfig = {
|
||||
success: {
|
||||
color: "#00FF00",
|
||||
icon: "checkMark",
|
||||
fallback: "✓",
|
||||
label: "Success",
|
||||
},
|
||||
error: {
|
||||
color: "#FF0000",
|
||||
icon: "crossMark",
|
||||
fallback: "✗",
|
||||
label: "Error",
|
||||
},
|
||||
warning: {
|
||||
color: "#FFFF00",
|
||||
icon: "warning",
|
||||
fallback: "!",
|
||||
label: "Warning",
|
||||
},
|
||||
info: {
|
||||
color: "#00FFFF",
|
||||
icon: "info",
|
||||
fallback: "i",
|
||||
label: "Info",
|
||||
},
|
||||
loading: {
|
||||
color: "#0080FF",
|
||||
icon: "spinner",
|
||||
fallback: "...",
|
||||
label: "Loading",
|
||||
animated: true,
|
||||
},
|
||||
idle: {
|
||||
color: "#808080",
|
||||
icon: "circle",
|
||||
fallback: "○",
|
||||
label: "Idle",
|
||||
},
|
||||
connected: {
|
||||
color: "#00FF00",
|
||||
icon: "filledCircle",
|
||||
fallback: "●",
|
||||
label: "Connected",
|
||||
},
|
||||
disconnected: {
|
||||
color: "#FF0000",
|
||||
icon: "circle",
|
||||
fallback: "○",
|
||||
label: "Disconnected",
|
||||
},
|
||||
processing: {
|
||||
color: "#FF8000",
|
||||
icon: "spinner",
|
||||
fallback: "⟳",
|
||||
label: "Processing",
|
||||
animated: true,
|
||||
},
|
||||
};
|
||||
|
||||
const config = statusConfig[status] || statusConfig.idle;
|
||||
const shouldAnimate = animated || config.animated;
|
||||
|
||||
// Animation effect
|
||||
React.useEffect(() => {
|
||||
if (!shouldAnimate) return;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
setAnimationFrame((frame) => (frame + 1) % 8);
|
||||
}, 200);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [shouldAnimate]);
|
||||
|
||||
// Generate status icon
|
||||
const generateIcon = () => {
|
||||
let icon;
|
||||
|
||||
if (customIcon) {
|
||||
icon = customIcon;
|
||||
} else if (config.icon === "spinner" && shouldAnimate) {
|
||||
icon = utils.createSpinner(animationFrame);
|
||||
} else {
|
||||
icon = unicode.getChar("symbols", config.icon, config.fallback);
|
||||
}
|
||||
|
||||
const iconColor =
|
||||
customColor ||
|
||||
(capabilities.trueColor
|
||||
? colors.getInkColor(config.color)
|
||||
: config.color.toLowerCase().replace("#", ""));
|
||||
|
||||
const sizeStyle = {
|
||||
small: {},
|
||||
medium: { bold: true },
|
||||
large: { bold: true },
|
||||
};
|
||||
|
||||
return (
|
||||
<Text color={iconColor} {...sizeStyle[size]}>
|
||||
{icon}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
// Generate status label
|
||||
const generateLabel = () => {
|
||||
if (!showLabel) return null;
|
||||
|
||||
const labelText = label || config.label;
|
||||
const labelColor = capabilities.trueColor
|
||||
? colors.getInkColor("#FFFFFF")
|
||||
: "white";
|
||||
|
||||
return (
|
||||
<Text color={labelColor} marginLeft={1}>
|
||||
{labelText}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box flexDirection="row" alignItems="center" {...props}>
|
||||
{generateIcon()}
|
||||
{generateLabel()}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Connection status indicator with pulse animation
|
||||
*/
|
||||
const ModernConnectionStatus = ({
|
||||
isConnected = false,
|
||||
label = "",
|
||||
showDetails = false,
|
||||
details = {},
|
||||
...props
|
||||
}) => {
|
||||
const { colors, unicode, capabilities } = useModernTerminal();
|
||||
const [pulseFrame, setPulseFrame] = React.useState(0);
|
||||
|
||||
// Pulse animation for connected state
|
||||
React.useEffect(() => {
|
||||
if (!isConnected) return;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
setPulseFrame((frame) => (frame + 1) % 6);
|
||||
}, 300);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [isConnected]);
|
||||
|
||||
const generateConnectionIcon = () => {
|
||||
if (isConnected) {
|
||||
// Pulsing connected indicator
|
||||
const intensity = Math.sin((pulseFrame / 6) * Math.PI * 2) * 0.3 + 0.7;
|
||||
const baseColor = capabilities.trueColor ? "#00FF00" : "green";
|
||||
|
||||
// For true color terminals, we could adjust brightness
|
||||
// For now, just use the base color
|
||||
const icon = unicode.getChar("symbols", "filledCircle", "●");
|
||||
|
||||
return (
|
||||
<Text color={colors.getInkColor(baseColor)} bold>
|
||||
{icon}
|
||||
</Text>
|
||||
);
|
||||
} else {
|
||||
// Disconnected indicator
|
||||
const icon = unicode.getChar("symbols", "circle", "○");
|
||||
const color = capabilities.trueColor
|
||||
? colors.getInkColor("#FF0000")
|
||||
: "red";
|
||||
|
||||
return <Text color={color}>{icon}</Text>;
|
||||
}
|
||||
};
|
||||
|
||||
const generateDetails = () => {
|
||||
if (!showDetails || !details) return null;
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" marginLeft={2}>
|
||||
{Object.entries(details).map(([key, value]) => (
|
||||
<Text key={key} color="gray">
|
||||
{key}: {value}
|
||||
</Text>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" {...props}>
|
||||
<Box flexDirection="row" alignItems="center">
|
||||
{generateConnectionIcon()}
|
||||
<Text marginLeft={1}>
|
||||
{label || (isConnected ? "Connected" : "Disconnected")}
|
||||
</Text>
|
||||
</Box>
|
||||
{generateDetails()}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Multi-state status indicator
|
||||
*/
|
||||
const ModernMultiStateIndicator = ({
|
||||
states = [],
|
||||
currentState = 0,
|
||||
showProgress = false,
|
||||
orientation = "horizontal",
|
||||
...props
|
||||
}) => {
|
||||
const { colors, unicode, capabilities } = useModernTerminal();
|
||||
|
||||
const generateStateIndicators = () => {
|
||||
return states.map((state, index) => {
|
||||
const isActive = index === currentState;
|
||||
const isCompleted = index < currentState;
|
||||
const isPending = index > currentState;
|
||||
|
||||
let icon, color;
|
||||
|
||||
if (isCompleted) {
|
||||
icon = unicode.getChar("symbols", "checkMark", "✓");
|
||||
color = capabilities.trueColor
|
||||
? colors.getInkColor("#00FF00")
|
||||
: "green";
|
||||
} else if (isActive) {
|
||||
icon = unicode.getChar("symbols", "pointer", "►");
|
||||
color = capabilities.trueColor ? colors.getInkColor("#0080FF") : "blue";
|
||||
} else {
|
||||
icon = unicode.getChar("symbols", "circle", "○");
|
||||
color = capabilities.trueColor ? colors.getInkColor("#808080") : "gray";
|
||||
}
|
||||
|
||||
const connector =
|
||||
index < states.length - 1 ? (
|
||||
<Text color="gray" marginX={1}>
|
||||
{orientation === "horizontal" ? "─" : "│"}
|
||||
</Text>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
<Box
|
||||
flexDirection={orientation === "horizontal" ? "row" : "column"}
|
||||
alignItems="center"
|
||||
>
|
||||
<Text color={color}>{icon}</Text>
|
||||
<Text marginLeft={1} color={isActive ? "white" : "gray"}>
|
||||
{state.label || `State ${index + 1}`}
|
||||
</Text>
|
||||
</Box>
|
||||
{connector}
|
||||
</React.Fragment>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const generateProgress = () => {
|
||||
if (!showProgress) return null;
|
||||
|
||||
const progress = ((currentState + 1) / states.length) * 100;
|
||||
|
||||
return (
|
||||
<Box marginTop={1}>
|
||||
<Text color="gray">
|
||||
Progress: {Math.round(progress)}% ({currentState + 1}/{states.length})
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" {...props}>
|
||||
<Box flexDirection={orientation === "horizontal" ? "row" : "column"}>
|
||||
{generateStateIndicators()}
|
||||
</Box>
|
||||
{generateProgress()}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Health status indicator with metrics
|
||||
*/
|
||||
const ModernHealthIndicator = ({
|
||||
health = "unknown",
|
||||
metrics = {},
|
||||
showMetrics = false,
|
||||
thresholds = {},
|
||||
...props
|
||||
}) => {
|
||||
const { colors, unicode, utils, capabilities } = useModernTerminal();
|
||||
|
||||
// Health status configurations
|
||||
const healthConfig = {
|
||||
healthy: {
|
||||
color: "#00FF00",
|
||||
icon: "checkMark",
|
||||
label: "Healthy",
|
||||
},
|
||||
degraded: {
|
||||
color: "#FFFF00",
|
||||
icon: "warning",
|
||||
label: "Degraded",
|
||||
},
|
||||
unhealthy: {
|
||||
color: "#FF0000",
|
||||
icon: "crossMark",
|
||||
label: "Unhealthy",
|
||||
},
|
||||
unknown: {
|
||||
color: "#808080",
|
||||
icon: "info",
|
||||
label: "Unknown",
|
||||
},
|
||||
};
|
||||
|
||||
const config = healthConfig[health] || healthConfig.unknown;
|
||||
|
||||
const generateHealthIcon = () => {
|
||||
const icon = unicode.getChar("symbols", config.icon, "?");
|
||||
const color = capabilities.trueColor
|
||||
? colors.getInkColor(config.color)
|
||||
: config.color.toLowerCase().replace("#", "");
|
||||
|
||||
return (
|
||||
<Text color={color} bold>
|
||||
{icon}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
const generateMetrics = () => {
|
||||
if (!showMetrics || !metrics) return null;
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" marginLeft={2} marginTop={1}>
|
||||
{Object.entries(metrics).map(([key, value]) => {
|
||||
const threshold = thresholds[key];
|
||||
let metricColor = "white";
|
||||
|
||||
if (threshold) {
|
||||
if (value > threshold.critical) {
|
||||
metricColor = "red";
|
||||
} else if (value > threshold.warning) {
|
||||
metricColor = "yellow";
|
||||
} else {
|
||||
metricColor = "green";
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Text key={key} color={metricColor}>
|
||||
{key}: {value}
|
||||
</Text>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" {...props}>
|
||||
<Box flexDirection="row" alignItems="center">
|
||||
{generateHealthIcon()}
|
||||
<Text marginLeft={1}>{config.label}</Text>
|
||||
</Box>
|
||||
{generateMetrics()}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
ModernStatusIndicator,
|
||||
ModernConnectionStatus,
|
||||
ModernMultiStateIndicator,
|
||||
ModernHealthIndicator,
|
||||
};
|
||||
Reference in New Issue
Block a user