Skip to Content
Veltix
FeaturesInteractions

Interactions

Veltix provides powerful interaction capabilities that enable dynamic data exploration and component communication.

Overview

Interaction features in Veltix include:

  • Component Linking: Connect components for coordinated interactions
  • Data Filtering: Filter data based on user interactions
  • Drill-down Analysis: Navigate from summary to detailed views
  • Cross-chart Highlighting: Highlight related data across multiple charts
  • Custom Interactions: Build custom interaction patterns

Component Linking

// Define component links const componentLinks = [ { source: 'sales-chart', target: 'region-table', type: 'filter', field: 'region' }, { source: 'time-filter', target: 'all-charts', type: 'time-range', field: 'date' } ]; // Using component links <Dashboard components={components} links={componentLinks} onInteraction={handleInteraction} />
// Different link types const linkTypes = { filter: { type: 'filter', field: 'category', operator: 'equals' }, highlight: { type: 'highlight', field: 'value', color: '#ff6b6b' }, drillDown: { type: 'drill-down', target: 'detail-view', field: 'id' }, crossFilter: { type: 'cross-filter', fields: ['region', 'product'], operator: 'and' } };

Data Filtering

Interactive Filters

// Filter component function FilterComponent({ data, onFilterChange }) { const [filters, setFilters] = useState({}); const handleFilterChange = (field, value) => { const newFilters = { ...filters, [field]: value }; setFilters(newFilters); onFilterChange(newFilters); }; return ( <div className="filter-panel"> <Select label="Region" options={getUniqueValues(data, 'region')} onChange={(value) => handleFilterChange('region', value)} /> <Select label="Product" options={getUniqueValues(data, 'product')} onChange={(value) => handleFilterChange('product', value)} /> <DateRangePicker label="Date Range" onChange={(range) => handleFilterChange('dateRange', range)} /> </div> ); } // Using filters function FilteredDashboard() { const [filteredData, setFilteredData] = useState(originalData); const handleFilterChange = (filters) => { const filtered = applyFilters(originalData, filters); setFilteredData(filtered); }; return ( <div> <FilterComponent data={originalData} onFilterChange={handleFilterChange} /> <BarChart data={filteredData} /> <LineChart data={filteredData} /> </div> ); }

Advanced Filtering

// Advanced filter implementation const useAdvancedFiltering = (data, filters) => { const filteredData = useMemo(() => { return data.filter(item => { return Object.entries(filters).every(([field, filter]) => { if (!filter.value) return true; switch (filter.operator) { case 'equals': return item[field] === filter.value; case 'contains': return item[field].includes(filter.value); case 'greaterThan': return item[field] > filter.value; case 'lessThan': return item[field] < filter.value; case 'between': return item[field] >= filter.min && item[field] <= filter.max; case 'in': return filter.value.includes(item[field]); default: return true; } }); }); }, [data, filters]); return filteredData; };

Drill-down Analysis

Hierarchical Navigation

// Drill-down component function DrillDownChart({ data, hierarchy }) { const [currentLevel, setCurrentLevel] = useState(0); const [drillPath, setDrillPath] = useState([]); const handleDrillDown = (item) => { const newPath = [...drillPath, item]; setDrillPath(newPath); setCurrentLevel(currentLevel + 1); }; const handleDrillUp = () => { const newPath = drillPath.slice(0, -1); setDrillPath(newPath); setCurrentLevel(currentLevel - 1); }; const currentData = getDataAtLevel(data, drillPath); return ( <div> <div className="drill-navigation"> <button onClick={handleDrillUp} disabled={currentLevel === 0}> ← Back </button> <span>Level: {currentLevel}</span> <span>Path: {drillPath.map(p => p.name).join(' > ')}</span> </div> <BarChart data={currentData} onClick={handleDrillDown} drillable={true} /> </div> ); }

Multi-level Drill-down

// Multi-level drill-down configuration const drillDownConfig = { levels: [ { name: 'Country', field: 'country', chart: 'BarChart' }, { name: 'Region', field: 'region', chart: 'BarChart' }, { name: 'City', field: 'city', chart: 'BarChart' }, { name: 'Store', field: 'store', chart: 'TableChart' } ], breadcrumb: true, maxLevels: 4 }; // Using multi-level drill-down <DrillDownChart data={salesData} config={drillDownConfig} onDrillComplete={handleDrillComplete} />

Cross-chart Highlighting

Highlight Coordination

// Cross-chart highlighting const useCrossChartHighlighting = () => { const [highlightedItems, setHighlightedItems] = useState([]); const [highlightSource, setHighlightSource] = useState(null); const highlightItems = useCallback((items, source) => { setHighlightedItems(items); setHighlightSource(source); }, []); const clearHighlight = useCallback(() => { setHighlightedItems([]); setHighlightSource(null); }, []); return { highlightedItems, highlightSource, highlightItems, clearHighlight }; }; // Using cross-chart highlighting function HighlightedDashboard() { const { highlightedItems, highlightItems, clearHighlight } = useCrossChartHighlighting(); return ( <div> <BarChart data={salesData} highlightedItems={highlightedItems} onItemHover={(item) => highlightItems([item], 'sales-chart')} onItemLeave={clearHighlight} /> <PieChart data={categoryData} highlightedItems={highlightedItems} onItemHover={(item) => highlightItems([item], 'category-chart')} onItemLeave={clearHighlight} /> </div> ); }

Highlight Styles

// Highlight style configuration const highlightStyles = { default: { opacity: 0.3, stroke: '#666', strokeWidth: 1 }, highlighted: { opacity: 1, stroke: '#ff6b6b', strokeWidth: 2, shadow: '0 0 10px rgba(255, 107, 107, 0.5)' }, selected: { opacity: 1, stroke: '#4ecdc4', strokeWidth: 3, shadow: '0 0 15px rgba(78, 205, 196, 0.7)' } }; // Applying highlight styles <BarChart data={data} highlightStyles={highlightStyles} highlightedItems={highlightedItems} />

Custom Interactions

Event Handlers

// Custom interaction handlers const useCustomInteractions = () => { const [interactionState, setInteractionState] = useState({}); const handleClick = useCallback((event, data) => { console.log('Chart clicked:', data); setInteractionState(prev => ({ ...prev, lastClicked: data, clickCount: (prev.clickCount || 0) + 1 })); }, []); const handleHover = useCallback((event, data) => { setInteractionState(prev => ({ ...prev, hoveredItem: data })); }, []); const handleSelection = useCallback((event, selection) => { setInteractionState(prev => ({ ...prev, selectedItems: selection })); }, []); return { interactionState, handleClick, handleHover, handleSelection }; }; // Using custom interactions function InteractiveChart({ data }) { const { interactionState, handleClick, handleHover, handleSelection } = useCustomInteractions(); return ( <div> <BarChart data={data} onClick={handleClick} onHover={handleHover} onSelection={handleSelection} /> <div className="interaction-info"> <p>Clicked: {interactionState.lastClicked?.name}</p> <p>Hovered: {interactionState.hoveredItem?.name}</p> <p>Selected: {interactionState.selectedItems?.length || 0} items</p> </div> </div> ); }

Custom Interaction Patterns

// Custom interaction pattern const useInteractionPattern = (pattern) => { const [patternState, setPatternState] = useState({}); const executePattern = useCallback((trigger, data) => { switch (pattern.type) { case 'cascade': return executeCascadePattern(pattern, trigger, data); case 'synchronize': return executeSynchronizePattern(pattern, trigger, data); case 'conditional': return executeConditionalPattern(pattern, trigger, data); default: return null; } }, [pattern]); return { patternState, executePattern }; }; // Cascade interaction pattern const cascadePattern = { type: 'cascade', steps: [ { trigger: 'click', action: 'filter', target: 'all-charts', field: 'category' }, { trigger: 'filter', action: 'highlight', target: 'related-charts', field: 'value' }, { trigger: 'highlight', action: 'update-summary', target: 'summary-panel' } ] };

Interaction Configuration

Global Interaction Settings

// Global interaction configuration const interactionConfig = { enabled: true, mode: 'cross-chart', // 'single-chart', 'cross-chart', 'custom' highlightOnHover: true, selectOnClick: true, enableDrillDown: true, enableCrossFiltering: true, animation: { duration: 300, easing: 'ease-in-out' }, styles: { highlight: { color: '#ff6b6b', opacity: 0.8 }, selection: { color: '#4ecdc4', opacity: 0.9 } } }; // Apply global configuration <Dashboard components={components} interactionConfig={interactionConfig} />

Component-specific Interactions

// Component-specific interaction settings const componentInteractions = { 'sales-chart': { clickable: true, hoverable: true, selectable: true, drillable: true, onInteraction: (event, data) => { console.log('Sales chart interaction:', event, data); } }, 'region-table': { clickable: true, hoverable: false, selectable: true, drillable: false, onInteraction: (event, data) => { console.log('Region table interaction:', event, data); } } };

Performance Optimization

Interaction Performance

// Optimized interaction handling const useOptimizedInteractions = (data, config) => { const [interactionState, setInteractionState] = useState({}); const interactionRef = useRef({}); const handleInteraction = useCallback((event, data) => { // Debounce frequent interactions if (interactionRef.current.timeout) { clearTimeout(interactionRef.current.timeout); } interactionRef.current.timeout = setTimeout(() => { setInteractionState(prev => ({ ...prev, [event.type]: data, timestamp: Date.now() })); }, config.debounce || 100); }, [config.debounce]); // Cleanup on unmount useEffect(() => { return () => { if (interactionRef.current.timeout) { clearTimeout(interactionRef.current.timeout); } }; }, []); return { interactionState, handleInteraction }; };

Memory Management

// Memory-efficient interaction state const useInteractionState = (maxHistory = 10) => { const [history, setHistory] = useState([]); const [currentState, setCurrentState] = useState({}); const addToHistory = useCallback((interaction) => { setHistory(prev => { const newHistory = [...prev, interaction]; // Keep only the latest interactions return newHistory.slice(-maxHistory); }); }, [maxHistory]); const clearHistory = useCallback(() => { setHistory([]); }, []); return { history, currentState, addToHistory, clearHistory, setCurrentState }; };

Best Practices

1. Interaction Design

  • Keep interactions intuitive and discoverable
  • Provide visual feedback for all interactions
  • Use consistent interaction patterns
  • Consider accessibility requirements

2. Performance

  • Debounce frequent interactions
  • Limit interaction history
  • Optimize data filtering
  • Use efficient event handling

3. User Experience

  • Provide clear interaction feedback
  • Include interaction instructions
  • Support keyboard navigation
  • Maintain interaction state

4. Accessibility

  • Support screen readers
  • Provide keyboard shortcuts
  • Include ARIA labels
  • Ensure color contrast

5. Testing

  • Test all interaction paths
  • Verify cross-browser compatibility
  • Test with different data sizes
  • Validate interaction performance

Troubleshooting

Common Issues

Interactions not working

  • Check interaction configuration
  • Verify event handlers
  • Test component connectivity
  • Review console errors

Performance issues

  • Implement interaction debouncing
  • Optimize data filtering
  • Limit interaction history
  • Monitor memory usage

Cross-chart issues

  • Verify component linking
  • Check data consistency
  • Test interaction propagation
  • Review highlight styles
Last updated on