78 lines
2.3 KiB
TypeScript
78 lines
2.3 KiB
TypeScript
import { useEffect, useRef, useCallback } from 'react';
|
||
import { LogEntry } from '../../api/types';
|
||
import { LogEntry as LogEntryComponent } from './LogEntry';
|
||
import { cn } from '../../utils/helpers';
|
||
|
||
interface LogStreamProps {
|
||
logs: LogEntry[];
|
||
isLoading?: boolean;
|
||
isLive?: boolean;
|
||
autoScroll?: boolean;
|
||
className?: string;
|
||
maxHeight?: string;
|
||
}
|
||
|
||
export function LogStream({
|
||
logs,
|
||
isLoading = false,
|
||
isLive = false,
|
||
autoScroll = true,
|
||
className,
|
||
maxHeight = '500px',
|
||
}: LogStreamProps) {
|
||
const containerRef = useRef<HTMLDivElement>(null);
|
||
const prevLogsLength = useRef(logs.length);
|
||
|
||
const scrollToBottom = useCallback(() => {
|
||
if (containerRef.current) {
|
||
containerRef.current.scrollTop = containerRef.current.scrollHeight;
|
||
}
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
if (autoScroll && logs.length > prevLogsLength.current) {
|
||
scrollToBottom();
|
||
}
|
||
prevLogsLength.current = logs.length;
|
||
}, [logs.length, autoScroll, scrollToBottom]);
|
||
|
||
return (
|
||
<div
|
||
ref={containerRef}
|
||
className={cn(
|
||
'overflow-y-auto rounded-lg border border-border bg-card font-mono text-sm',
|
||
className
|
||
)}
|
||
style={{ maxHeight }}
|
||
>
|
||
{isLoading && logs.length === 0 ? (
|
||
<div className="flex items-center justify-center p-8 text-muted-foreground">
|
||
Загрузка логов...
|
||
</div>
|
||
) : logs.length === 0 ? (
|
||
<div className="flex items-center justify-center p-8 text-muted-foreground">
|
||
Нет логов для отображения
|
||
</div>
|
||
) : (
|
||
<div className="divide-y divide-border">
|
||
{logs.map((log) => (
|
||
<LogEntryComponent key={log.id} log={log} />
|
||
))}
|
||
</div>
|
||
)}
|
||
|
||
{isLive && (
|
||
<div className="sticky bottom-0 left-0 right-0 flex items-center justify-center gap-2 border-t border-border bg-card/95 p-2 text-xs text-green-400 backdrop-blur">
|
||
<span className="relative flex h-2 w-2">
|
||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75" />
|
||
<span className="relative inline-flex h-2 w-2 rounded-full bg-green-400" />
|
||
</span>
|
||
Прямой эфир
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default LogStream;
|