-
Notifications
You must be signed in to change notification settings - Fork 7
Open
Labels
enhancementNew feature or requestNew feature or request
Milestone
Description
WIP
π― Problem Statement
Currently, @gravity-ui/graph lacks a unified system for programmatically managing component states (highlighting, focusing, prioritizing). Developers need to manually implement that.
π‘ Proposed Solution
Implement a Universal Highlight System that provides clean programming interfaces for managing component states
Core Principles
π Detailed Requirements
1. Two Fundamentally Different Highlight Modes
π― Mode 1: highlight() - Selective Highlighting
// ONLY highlights specified entities, others remain UNCHANGED
graph.highlight({
block: ['user-1', 'user-2'],
connection: ['critical-link']
});
// Result:
// user-1: HighlightVisualMode.Highlight (20)
// user-2: HighlightVisualMode.Highlight (20)
// critical-link: HighlightVisualMode.Highlight (20)
// ALL OTHER entities: undefined (normal state - untouched)π Mode 2: focus() - Spotlight with Dimming
// Highlights targets AND lowlights EVERYTHING ELSE
graph.focus({
block: ['important-node'],
connection: ['main-flow']
});
// Result:
// important-node: HighlightVisualMode.Highlight (20)
// main-flow: HighlightVisualMode.Highlight (20)
// ALL OTHER entities: HighlightVisualMode.Lowlight (10) β KEY DIFFERENCE!π Mode Management
// Clear all states - everything returns to normal
graph.clearHighlight();
// Result: ALL entities = undefined (normal state)β‘ Critical Differences Explained
| Aspect | highlight() |
focus() |
|---|---|---|
| Target entities | Highlight (20) |
Highlight (20) |
| Non-target entities | undefined (unchanged) |
Lowlight (10) (dimmed) |
| Use case | "Show important items" | "Focus attention, hide noise" |
| Performance | β Fast (only targets change) | |
| Visual impact | π― Selective emphasis | π Dramatic contrast |
2. Universal ID System
// Core prefixes (built-in library support)
block:user-123 // Blocks
connection:link-456 // Connections
anchor:user-123:output // Anchors
// User extensions (unlimited extensibility)
group:team-alpha // Custom groups
layer:background // Custom layers
plugin:my-element // Plugin components
myApp:special-feature // Application-specific3. Real-World Usage Scenarios
π― When to use highlight()
// β
Show search results (don't hide other data)
const searchResults = ['user-123', 'user-456'];
graph.highlight({ block: searchResults });
// β
Mark validation errors (keep context visible)
const invalidNodes = validateGraph();
graph.highlight({ block: invalidNodes.map(n => n.id) });
// β
Show related elements (preserve workflow context)
graph.highlight({
block: ['selected-node'],
connection: getConnectedEdges('selected-node'),
anchor: getRelatedAnchors('selected-node')
});π When to use focus()
// β
Critical path analysis (hide distractions)
const criticalPath = findCriticalPath();
graph.focus({
block: criticalPath.nodes,
connection: criticalPath.edges
});
// β
Debug specific workflow (isolate problem area)
graph.focus({
block: ['error-source'],
connection: ['failed-connection']
});
// β
Presentation mode (spotlight key elements)
graph.focus({ block: ['demo-node-1', 'demo-node-2'] });β‘ Performance Impact
// highlight() - FAST: Only changes target entities
graph.highlight({ block: ['node-1'] });
// Changes: 1 entity state
// Performance: O(targets)
// focus() - SLOWER: Changes ALL entities in graph
graph.focus({ block: ['node-1'] });
// Changes: ALL entity states (targets + non-targets)
// Performance: O(all entities)
// For large graphs (1000+ entities), consider highlight() for frequent operations4. Component Integration
// Extends
class GraphComponent {
// 1. Define unique ID
public abstract getHighlightId(): string {
return `myPlugin:${this.id}`;
}
afterInit() {
this.onSignal(
computed(() => this.store.graph.highlightService.getEntityHighlightMode(this.getHighlightId())),
(mode) => this.setState({ highlightMode: mode }),
);
}
}
class BaseConnection extends GraphComponent {
public getHighlightId(): string {
return `connection:${this.state.id}`;
}
}
class Block extends GraphComponent {
public getHighlightId(): string {
return `block:${this.state.id}`;
}
}
class Anchor extends GraphComponent {
public getHighlightId(): string {
return `anchor:${this.id}`;
}
}5. Internal State Management Logic
enum HighlightVisualMode {
Highlight = 20, // High priority state
Lowlight = 10, // Low priority state
// Normal = undefined (neutral state)
}
type HighlightServiceMode = 'highlight' | 'focus';
// Core logic - how getEntityHighlightMode() works differently
class HighlightService {
public getEntityHighlightMode(entityId: string): HighlightVisualMode | undefined {
const state = this.$state.value;
if (!state.active) return undefined;
const isTargeted = state.entities.has(entityId);
if (state.mode === 'highlight') {
// β¨ HIGHLIGHT MODE: Only targets get state, others stay normal
return isTargeted ? HighlightVisualMode.Highlight : undefined;
} else { // focus mode
// π₯ FOCUS MODE: Targets get highlighted, EVERYONE ELSE gets lowlighted
return isTargeted ? HighlightVisualMode.Highlight : HighlightVisualMode.Lowlight;
}
}
}π Mode Switching Behavior
// Starting state: everything normal (undefined)
// Initial: all entities = undefined
graph.highlight({ block: ['A', 'B'] });
// Result: A=Highlight(20), B=Highlight(20), others=undefined
graph.focus({ block: ['C'] }); // β REPLACES previous state!
// Result: C=Highlight(20), A=Lowlight(10), B=Lowlight(10), others=Lowlight(10)
graph.highlight({ connection: ['X'] }); // β REPLACES focus!
// Result: X=Highlight(20), A=undefined, B=undefined, C=undefined, others=undefined6. Event System
// React to highlight changes
graph.on('highlight-changed', (event) => {
console.log('Mode:', event.mode); // 'highlight' | 'focus' | undefined
console.log('Entities:', event.entities); // ['block:id1', 'connection:id2']
console.log('Previous:', event.previous); // Previous state
});π Usage Examples
Side-by-Side Mode Comparison
Scenario: Graph with 5 nodes (A, B, C, D, E)
// BEFORE: All nodes normal
// A=undefined, B=undefined, C=undefined, D=undefined, E=undefined
// =====================================================
// π― HIGHLIGHT MODE - Selective emphasis
// =====================================================
graph.highlight({ block: ['A', 'B'] });
// RESULT: Only targets change, others UNTOUCHED
// A=Highlight(20) β highlighted
// B=Highlight(20) β highlighted
// C=undefined β normal (unchanged)
// D=undefined β normal (unchanged)
// E=undefined β normal (unchanged)// =====================================================
// π FOCUS MODE - Dramatic spotlight effect
// =====================================================
graph.focus({ block: ['A', 'B'] });
// RESULT: Targets highlighted, EVERYTHING ELSE dimmed
// A=Highlight(20) β highlighted
// B=Highlight(20) β highlighted
// C=Lowlight(10) β dimmed β οΈ
// D=Lowlight(10) β dimmed β οΈ
// E=Lowlight(10) β dimmed β οΈReal-World Impact
| Graph Size | highlight() Changes |
focus() Changes |
|---|---|---|
| 10 entities | 2 entities | 10 entities (all!) |
| 100 entities | 5 entities | 100 entities (all!) |
| 1000 entities | 10 entities | 1000 entities (all!) |
π Performance: highlight() scales with targets, focus() scales with total graph size!
Visual State Diagram
INITIAL STATE: All entities normal
βββββ βββββ βββββ βββββ βββββ
β A β β B β β C β β D β β E β
βββββ βββββ βββββ βββββ βββββ
βͺ βͺ βͺ βͺ βͺ (all undefined)
graph.highlight({ block: ['A', 'B'] });
βββββ βββββ βββββ βββββ βββββ
β A β β B β β C β β D β β E β
βββββ βββββ βββββ βββββ βββββ
π’ π’ βͺ βͺ βͺ β Only targets change!
graph.focus({ block: ['A', 'B'] });
βββββ βββββ βββββ βββββ βββββ
β A β β B β β C β β D β β E β
βββββ βββββ βββββ βββββ βββββ
π’ π’ π΄ π΄ π΄ β ALL entities change!
Legend:
π’ Highlight(20) π΄ Lowlight(10) βͺ Normal(undefined)
External Integration
// Analytics integration
graph.on('highlight-changed', (event) => {
analytics.track('graph_highlight', {
mode: event.mode,
entityCount: event.entities.length
});
event.preventDefault(); // if you want to prevent default behavior
});Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request