1. Home 2. Components 3. Waterfall # Waterfall A hierarchical log display component for showing time-based log data with interactive controls and custom actions. ```typescript interface WaterfallProps { logData: LogItemType[]; temporalCursor?: number; panelWidth?: number; onTemporalCursorChange?: (time: number) => void; getIcon: (item: LogItemType) => ReactNode; hoveredId?: string | null; onItemHover?: (id: string | null) => void; minWindow?: number; maxWindow?: number; zoomFactor?: number; enabled?: boolean; children?: React.ReactNode; className?: string; } ``` ## Types ```typescript type LogItemType = TreeDataItemV2 & { type: "task" | "attempt" | "info" | "step"; icon?: "history" | "file-code" | "bot" | "check-circle" | "pause-circle"; createTime?: number; startTime?: number; duration?: number; time?: number; color?: "blue" | "green" | "orange" | "gray-light" | "gray-medium" | "purple"; isCollapsible?: boolean; hasStripes?: boolean; isHaltedStep?: boolean; }; type TreeDataItemV2 = { id: string; parentId: string | null; label: string; isCollapsible?: boolean; actions?: ReactNode; disable?: boolean; [key: string]: unknown; }; ``` ## Basic Usage Job registered in queue generate-report Attempt 1 Fetch database records Job halted, waiting for resources... Waiting for image renderer... Render charts Assemble PDF Fetch database records Memory checkpoint data-validation Schema validation Data integrity check Generate validation report Cache cleared email-notification Prepare email template Attach report files Send via SMTP Webhook triggered System health check cleanup-process Remove temporary files Update job status All tasks completed -10s -5s 0ms 5s 10s 15s 20s 25s 30s 20.000s 19.900s 3.000s Halted 4.600s 6.200s 3.000s 12.000s 3.500s 33.000s ```jsx import { Waterfall, LogItemType } from "@vuer-ai/vuer-uikit"; import React, { useState, useMemo } from "react"; import { Bot, CheckCircle2, FileCode, History, Info, PauseCircle, Eye, EyeClosed, Trash2, } from "lucide-react"; import { cn } from "@vuer-ai/vuer-uikit"; const logData: LogItemType[] = [ { id: "0", parentId: null, indent: 0, etype: "info", label: "Job registered in queue", icon: "history", time: 0, color: "purple", }, { id: "1", parentId: null, indent: 0, etype: "task", label: "generate-report", icon: "file-code", createTime: 0, startTime: 0, duration: 20, color: "blue", isCollapsible: true, hasStripes: true, }, { id: "2", parentId: "1", indent: 1, etype: "attempt", label: "Attempt 1", icon: "bot", createTime: 0.1, startTime: 0.1, duration: 19.9, color: "blue", isCollapsible: true, }, { id: "3", parentId: "2", indent: 2, etype: "step", label: "Fetch database records", icon: "check-circle", createTime: 0.2, startTime: 0.5, duration: 3, color: "green", }, { id: "4", parentId: "2", indent: 2, etype: "step", label: "Job halted, waiting for resources...", icon: "pause-circle", startTime: 4, duration: 2, color: "orange", isHaltedStep: true, }, // ... more log items ]; const getIcon = (item: LogItemType) => { const iconColor = (item: LogItemType) => { if (item.label === "generate-report" || item.label === "Assemble PDF") return "text-blue-500"; if (item.icon === "file-code") return "text-muted-foreground"; return ""; }; switch (item.icon) { case "history": return ; case "file-code": return ; case "bot": return ; case "check-circle": return ; case "pause-circle": return ; default: return ; } }; // Helper functions const getAllChildrenIds = (parentId: string, allItems: LogItemType[]): string[] => { const children: string[] = []; const directChildren = allItems.filter(item => item.parentId === parentId); for (const child of directChildren) { children.push(child.id); children.push(...getAllChildrenIds(child.id, allItems)); } return children; }; const isIndirectlyHidden = (itemId: string, hiddenItems: Set, allItems: LogItemType[]): boolean => { const item = allItems.find(i => i.id === itemId); if (!item || !item.parentId) return false; if (hiddenItems.has(item.parentId)) { return true; } return isIndirectlyHidden(item.parentId, hiddenItems, allItems); }; // State management const [expandedItems, setExpandedItems] = useState(new Set(["1", "2"])); const [hoveredId, setHoveredId] = useState(null); const [hiddenItems, setHiddenItems] = useState>(new Set()); const [deletedItems, setDeletedItems] = useState>(new Set()); // Toggle visibility function const toggleItemVisibility = (itemId: string) => { const wasHidden = hiddenItems.has(itemId); setHiddenItems(prev => { const newSet = new Set(prev); if (newSet.has(itemId)) { newSet.delete(itemId); } else { newSet.add(itemId); } return newSet; }); // If hiding an expanded item, collapse it if (!wasHidden && expandedItems.has(itemId)) { setExpandedItems(prev => { const newSet = new Set(prev); newSet.delete(itemId); return newSet; }); } }; // Delete item function const deleteItem = (itemId: string) => { const itemsToDelete = [itemId, ...getAllChildrenIds(itemId, logData)]; setDeletedItems(prev => { const newSet = new Set(prev); itemsToDelete.forEach(id => newSet.add(id)); return newSet; }); setHiddenItems(prev => { const newSet = new Set(prev); itemsToDelete.forEach(id => newSet.delete(id)); return newSet; }); setExpandedItems(prev => { const newSet = new Set(prev); itemsToDelete.forEach(id => newSet.delete(id)); return newSet; }); }; // Create actions for each item const createActions = (itemId: string, isDirectlyHidden: boolean, isIndirectlyHiddenItem: boolean, isHovered: boolean) => { const visibilityButton = ( ); const deleteButton = ( ); return (
{isHovered && deleteButton} {visibilityButton}
); }; // Process log data with actions and visibility states const processedLogData = useMemo(() => { return logData .filter(item => !deletedItems.has(item.id)) .map(item => { const isDirectlyHidden = hiddenItems.has(item.id); const isIndirectlyHiddenItem = isIndirectlyHidden(item.id, hiddenItems, logData); const isItemHidden = isDirectlyHidden || isIndirectlyHiddenItem; const isHovered = hoveredId === item.id; return { ...item, actions: createActions(item.id, isDirectlyHidden, isIndirectlyHiddenItem, isHovered), disable: isItemHidden, isSelectable: !isItemHidden, }; }); }, [logData, deletedItems, hiddenItems, hoveredId]); return (
); ```