polly-graph 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { SimulationNodeDatum, SimulationLinkDatum } from 'd3-force';
1
+ import { SimulationNodeDatum, SimulationLinkDatum, Simulation } from 'd3-force';
2
+ import { Selection } from 'd3-selection';
2
3
 
3
4
  interface Coordinates {
4
5
  readonly x: number;
@@ -32,7 +33,7 @@ interface LegendConfig {
32
33
  readonly enabled?: boolean;
33
34
  readonly title?: string;
34
35
  readonly position?: LegendPosition;
35
- readonly items: LegendItem[];
36
+ readonly items?: LegendItem[];
36
37
  readonly collapsible?: boolean;
37
38
  readonly defaultExpanded?: boolean;
38
39
  }
@@ -85,6 +86,101 @@ interface LinkStyle {
85
86
  readonly arrow?: LinkArrowStyle;
86
87
  readonly label?: LinkLabelStyle;
87
88
  }
89
+ interface ResolvedLinkLabelStyle {
90
+ readonly enabled: boolean;
91
+ readonly visibility: 'always' | 'hover' | 'selection';
92
+ readonly backgroundFill: string;
93
+ readonly borderColor: string;
94
+ readonly borderWidth: number;
95
+ readonly borderRadius: number;
96
+ readonly textColor: string;
97
+ readonly fontSize: number;
98
+ readonly paddingX: number;
99
+ readonly paddingY: number;
100
+ readonly height: number;
101
+ }
102
+ interface ResolvedLinkStyle {
103
+ readonly stroke: string;
104
+ readonly strokeWidth: number;
105
+ readonly opacity: number;
106
+ readonly dashArray: string | undefined;
107
+ readonly arrow: {
108
+ readonly enabled: boolean;
109
+ readonly size: number;
110
+ readonly fill: string;
111
+ };
112
+ readonly label: ResolvedLinkLabelStyle;
113
+ }
114
+
115
+ interface LinkForceConfig {
116
+ readonly enabled?: boolean;
117
+ readonly distance?: number | ((link: GraphLink) => number);
118
+ readonly strength?: number | ((link: GraphLink) => number);
119
+ readonly iterations?: number;
120
+ }
121
+ interface ChargeForceConfig {
122
+ readonly enabled?: boolean;
123
+ readonly strength?: number | ((node: GraphNode) => number);
124
+ readonly theta?: number;
125
+ readonly distanceMin?: number;
126
+ readonly distanceMax?: number;
127
+ }
128
+ interface CollideForceConfig {
129
+ readonly enabled?: boolean;
130
+ readonly radius?: number | ((node: GraphNode) => number);
131
+ readonly strength?: number;
132
+ readonly iterations?: number;
133
+ }
134
+ interface CenterForceConfig {
135
+ readonly enabled?: boolean;
136
+ readonly strength?: number;
137
+ readonly x?: number;
138
+ readonly y?: number;
139
+ }
140
+ interface XForceConfig {
141
+ readonly enabled?: boolean;
142
+ readonly strength?: number;
143
+ readonly x?: number | ((node: GraphNode) => number);
144
+ }
145
+ interface YForceConfig {
146
+ readonly enabled?: boolean;
147
+ readonly strength?: number;
148
+ readonly y?: number | ((node: GraphNode) => number);
149
+ }
150
+ interface SimulationForces {
151
+ readonly link?: LinkForceConfig;
152
+ readonly charge?: ChargeForceConfig;
153
+ readonly collide?: CollideForceConfig;
154
+ readonly center?: CenterForceConfig;
155
+ readonly x?: XForceConfig;
156
+ readonly y?: YForceConfig;
157
+ }
158
+ interface AdaptiveConfig {
159
+ readonly enabled?: boolean;
160
+ readonly nodeCountThresholds?: {
161
+ readonly small?: number;
162
+ readonly medium?: number;
163
+ readonly large?: number;
164
+ };
165
+ }
166
+ interface WarmupConfig {
167
+ readonly enabled?: boolean;
168
+ readonly ticks?: number;
169
+ }
170
+ interface CooldownConfig {
171
+ readonly enabled?: boolean;
172
+ readonly delay?: number;
173
+ }
174
+ interface EnhancedSimulationConfig {
175
+ readonly alpha?: number;
176
+ readonly alphaMin?: number;
177
+ readonly alphaDecay?: number;
178
+ readonly velocityDecay?: number;
179
+ readonly forces?: SimulationForces;
180
+ readonly adaptive?: AdaptiveConfig;
181
+ readonly warmup?: WarmupConfig;
182
+ readonly cooldown?: CooldownConfig;
183
+ }
88
184
 
89
185
  type GraphTooltipTheme = 'dark' | 'light';
90
186
  type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right' | 'auto';
@@ -120,11 +216,7 @@ interface GraphConfig {
120
216
  readonly links: GraphLink[];
121
217
  readonly autoFit?: boolean;
122
218
  readonly responsive?: boolean;
123
- readonly simulation?: {
124
- readonly alpha?: number;
125
- readonly gravity?: number;
126
- readonly linkDistance?: number | ((link: GraphLink) => number);
127
- };
219
+ readonly simulation?: EnhancedSimulationConfig;
128
220
  readonly interaction?: GraphInteractionConfig;
129
221
  readonly controls?: GraphControlsConfig;
130
222
  readonly legend?: LegendConfig;
@@ -138,12 +230,399 @@ interface GraphInstance {
138
230
  fitView(): void;
139
231
  destroy(): void;
140
232
  exportGraph(fileName?: string): void;
233
+ clearSelection(): void;
141
234
  on(event: 'nodeSelect', handler: (node: GraphNode, element: SVGCircleElement) => void): () => void;
235
+ on(event: 'nodeDeselect', handler: (node: GraphNode, element: SVGCircleElement) => void): () => void;
142
236
  on(event: 'linkSelect', handler: (link: GraphLink, element: SVGLineElement) => void): () => void;
237
+ on(event: 'linkDeselect', handler: (link: GraphLink, element: SVGLineElement) => void): () => void;
143
238
  off(event: 'nodeSelect', handler: (node: GraphNode, element: SVGCircleElement) => void): void;
239
+ off(event: 'nodeDeselect', handler: (node: GraphNode, element: SVGCircleElement) => void): void;
144
240
  off(event: 'linkSelect', handler: (link: GraphLink, element: SVGLineElement) => void): void;
241
+ off(event: 'linkDeselect', handler: (link: GraphLink, element: SVGLineElement) => void): void;
145
242
  }
146
243
 
244
+ /**
245
+ * Main Graph Factory - Creates and manages a graph instance
246
+ *
247
+ * Clean modular architecture:
248
+ * - GraphManager: State management and coordination
249
+ * - RenderPipeline: Rendering workflow
250
+ * - InteractionManager: User interactions
251
+ */
147
252
  declare function createGraph(config: GraphConfig): GraphInstance;
148
253
 
149
- export { type GraphConfig, type GraphInstance, type GraphInteractionConfig, type GraphLink, type GraphNode, createGraph };
254
+ /**
255
+ * Comprehensive validation system for framework-independent graph configuration.
256
+ * Provides runtime validation to catch integration errors early.
257
+ */
258
+
259
+ interface ValidationError {
260
+ readonly field: string;
261
+ readonly message: string;
262
+ readonly value: unknown;
263
+ readonly code: string;
264
+ }
265
+ interface ValidationResult {
266
+ readonly isValid: boolean;
267
+ readonly errors: ValidationError[];
268
+ readonly warnings: string[];
269
+ }
270
+ declare class GraphValidator {
271
+ /**
272
+ * Validate complete graph configuration
273
+ */
274
+ static validateConfig(config: GraphConfig): ValidationResult;
275
+ /**
276
+ * Validate container element
277
+ */
278
+ private static validateContainer;
279
+ /**
280
+ * Validate graph nodes
281
+ */
282
+ private static validateNodes;
283
+ /**
284
+ * Validate graph links
285
+ */
286
+ private static validateLinks;
287
+ /**
288
+ * Validate interaction configuration
289
+ */
290
+ private static validateInteraction;
291
+ /**
292
+ * Validate node style
293
+ */
294
+ private static validateNodeStyle;
295
+ /**
296
+ * Validate runtime environment
297
+ */
298
+ static validateEnvironment(): ValidationResult;
299
+ }
300
+ /**
301
+ * Validation error for framework integrations
302
+ */
303
+ declare class GraphValidationError extends Error {
304
+ readonly errors: ValidationError[];
305
+ readonly warnings: string[];
306
+ constructor(result: ValidationResult);
307
+ }
308
+
309
+ /**
310
+ * Centralized error handling for framework-independent graph operations.
311
+ * Provides graceful degradation and helpful error messages.
312
+ */
313
+
314
+ interface ErrorContext {
315
+ readonly operation: string;
316
+ readonly component?: string;
317
+ readonly data?: unknown;
318
+ }
319
+ declare class GraphError extends Error {
320
+ readonly code: string;
321
+ readonly context: ErrorContext;
322
+ readonly recoverable: boolean;
323
+ constructor(message: string, code: string, context: ErrorContext, recoverable?: boolean);
324
+ }
325
+ declare class ErrorHandler {
326
+ private static isDestroyed;
327
+ /**
328
+ * Handle errors with appropriate logging and recovery
329
+ */
330
+ static handle(error: Error, context: ErrorContext, fallback?: () => void): void;
331
+ /**
332
+ * Wrap async operations with error handling
333
+ */
334
+ static wrapAsync<T>(operation: () => Promise<T>, context: ErrorContext, fallback?: () => T): Promise<T>;
335
+ /**
336
+ * Wrap synchronous operations with error handling
337
+ */
338
+ static wrap<T>(operation: () => T, context: ErrorContext, fallback?: () => T): T | undefined;
339
+ /**
340
+ * Safe DOM operation wrapper
341
+ */
342
+ static safeDOMOperation<T>(operation: () => T, context: ErrorContext, fallback?: () => T): T | undefined;
343
+ /**
344
+ * Safe D3 operation wrapper
345
+ */
346
+ static safeD3Operation<T>(operation: () => T, context: ErrorContext, fallback?: () => T): T | undefined;
347
+ /**
348
+ * Create recoverable error for non-critical failures
349
+ */
350
+ static createRecoverableError(message: string, code: string, context: ErrorContext): GraphError;
351
+ /**
352
+ * Create non-recoverable error for critical failures
353
+ */
354
+ static createCriticalError(message: string, code: string, context: ErrorContext): GraphError;
355
+ /**
356
+ * Validate and handle D3 selection operations
357
+ */
358
+ static validateSelection<T extends {
359
+ size(): number;
360
+ }>(selection: T | null | undefined, context: ErrorContext, operation: (sel: T) => void): void;
361
+ /**
362
+ * Handle simulation errors with graceful degradation
363
+ */
364
+ static handleSimulationError(error: Error, context: ErrorContext, simulation?: Simulation<GraphNode, GraphLink>): void;
365
+ /**
366
+ * Mark error handler as destroyed to prevent further operations
367
+ */
368
+ static destroy(): void;
369
+ /**
370
+ * Reset error handler state
371
+ */
372
+ static reset(): void;
373
+ }
374
+
375
+ /**
376
+ * Framework-independent event emitter for graph events.
377
+ * Provides better TypeScript support and event namespacing than manual Set management.
378
+ */
379
+
380
+ type EventListener<T> = (data: T, element?: Element) => void;
381
+ interface EventUnsubscribe {
382
+ (): void;
383
+ }
384
+ interface EventEmitterOptions {
385
+ readonly maxListeners?: number;
386
+ readonly enableWarnings?: boolean;
387
+ }
388
+ declare class GraphEventEmitter<TEventMap extends Record<string, unknown>> {
389
+ private readonly listeners;
390
+ private readonly options;
391
+ private isDestroyed;
392
+ private listenerIdCounter;
393
+ constructor(options?: EventEmitterOptions);
394
+ /**
395
+ * Add an event listener
396
+ */
397
+ on<TEvent extends keyof TEventMap>(event: TEvent, listener: EventListener<TEventMap[TEvent]>, namespace?: string): EventUnsubscribe;
398
+ /**
399
+ * Add a one-time event listener
400
+ */
401
+ once<TEvent extends keyof TEventMap>(event: TEvent, listener: EventListener<TEventMap[TEvent]>, namespace?: string): EventUnsubscribe;
402
+ /**
403
+ * Remove a specific listener
404
+ */
405
+ off<TEvent extends keyof TEventMap>(event: TEvent, listener: EventListener<TEventMap[TEvent]>): void;
406
+ /**
407
+ * Remove listener by ID
408
+ */
409
+ private removeListener;
410
+ /**
411
+ * Remove all listeners for an event or namespace
412
+ */
413
+ removeAllListeners<TEvent extends keyof TEventMap>(event?: TEvent, namespace?: string): void;
414
+ /**
415
+ * Emit an event to all listeners
416
+ */
417
+ emit<TEvent extends keyof TEventMap>(event: TEvent, data: TEventMap[TEvent], element?: Element): boolean;
418
+ /**
419
+ * Get the number of listeners for an event
420
+ */
421
+ listenerCount<TEvent extends keyof TEventMap>(event: TEvent): number;
422
+ /**
423
+ * Get all event names that have listeners
424
+ */
425
+ eventNames(): Array<keyof TEventMap>;
426
+ /**
427
+ * Get listeners for an event
428
+ */
429
+ getListeners<TEvent extends keyof TEventMap>(event: TEvent): Array<EventListener<TEventMap[TEvent]>>;
430
+ /**
431
+ * Check if an event has listeners
432
+ */
433
+ hasListeners<TEvent extends keyof TEventMap>(event: TEvent): boolean;
434
+ /**
435
+ * Get debug information about the emitter
436
+ */
437
+ getDebugInfo(): {
438
+ totalEvents: number;
439
+ totalListeners: number;
440
+ events: {
441
+ [k: string]: number;
442
+ };
443
+ namespaces: {
444
+ [k: string]: number;
445
+ };
446
+ isDestroyed: boolean;
447
+ };
448
+ /**
449
+ * Clean up all listeners and mark as destroyed
450
+ */
451
+ destroy(): void;
452
+ /**
453
+ * Reset the emitter (clear listeners but allow new ones)
454
+ */
455
+ reset(): void;
456
+ }
457
+ /**
458
+ * Graph-specific event types
459
+ */
460
+ interface GraphEventMap {
461
+ 'nodeSelect': {
462
+ node: GraphNode;
463
+ element: SVGCircleElement;
464
+ };
465
+ 'nodeDeselect': {
466
+ node: GraphNode;
467
+ element: SVGCircleElement;
468
+ };
469
+ 'linkSelect': {
470
+ link: GraphLink;
471
+ element: SVGLineElement;
472
+ };
473
+ 'linkDeselect': {
474
+ link: GraphLink;
475
+ element: SVGLineElement;
476
+ };
477
+ 'graphRender': {
478
+ nodeCount: number;
479
+ linkCount: number;
480
+ };
481
+ 'simulationStart': {
482
+ alpha: number;
483
+ };
484
+ 'simulationEnd': {
485
+ iterations: number;
486
+ };
487
+ 'zoomStart': {
488
+ scale: number;
489
+ x: number;
490
+ y: number;
491
+ };
492
+ 'zoomEnd': {
493
+ scale: number;
494
+ x: number;
495
+ y: number;
496
+ };
497
+ [key: string]: unknown;
498
+ }
499
+ type GraphEventName = keyof GraphEventMap;
500
+ /**
501
+ * Type-safe graph event emitter
502
+ */
503
+ declare class TypedGraphEventEmitter extends GraphEventEmitter<GraphEventMap> {
504
+ constructor(options?: EventEmitterOptions);
505
+ }
506
+
507
+ interface GraphLayers {
508
+ readonly svg: SVGSVGElement;
509
+ readonly overlay: HTMLDivElement;
510
+ readonly interactionLayer: SVGGElement;
511
+ readonly interactionRect: SVGRectElement;
512
+ readonly root: SVGGElement;
513
+ readonly links: SVGGElement;
514
+ readonly nodeRings: SVGGElement;
515
+ readonly nodes: SVGGElement;
516
+ readonly nodeLabels: SVGGElement;
517
+ readonly linkLabels: SVGGElement;
518
+ readonly hoverLayer: {
519
+ readonly container: SVGGElement;
520
+ readonly links: SVGGElement;
521
+ readonly nodes: SVGGElement;
522
+ readonly nodeLabels: SVGGElement;
523
+ readonly linkLabels: SVGGElement;
524
+ };
525
+ readonly selectionLayer: {
526
+ readonly container: SVGGElement;
527
+ readonly links: SVGGElement;
528
+ readonly nodes: SVGGElement;
529
+ readonly nodeLabels: SVGGElement;
530
+ readonly linkLabels: SVGGElement;
531
+ };
532
+ }
533
+
534
+ interface RenderableGraphLink {
535
+ readonly link: GraphLink;
536
+ readonly style: ResolvedLinkStyle;
537
+ readonly markerEnd: string;
538
+ }
539
+
540
+ /**
541
+ * Centralized selection management for graph nodes and links.
542
+ * Simplifies selection logic and ensures consistent behavior.
543
+ */
544
+
545
+ interface SelectionState {
546
+ selectedNode: {
547
+ element: SVGCircleElement;
548
+ data: GraphNode;
549
+ } | null;
550
+ selectedLink: {
551
+ element: SVGLineElement;
552
+ data: GraphLink;
553
+ originalMarker: string | null;
554
+ } | null;
555
+ }
556
+ declare class SelectionManager {
557
+ private state;
558
+ private readonly eventEmitter;
559
+ private readonly config;
560
+ private readonly layers;
561
+ private readonly linkMarkerSnapshots;
562
+ private readonly root;
563
+ constructor(eventEmitter: TypedGraphEventEmitter, config: SelectionInteractionConfig, layers: GraphLayers, linkMarkerSnapshots: Map<SVGLineElement, string | null>, root: Selection<SVGGElement, unknown, null, undefined>);
564
+ /**
565
+ * Select a node, automatically deselecting any current selection
566
+ */
567
+ selectNode(nodeElement: SVGCircleElement, nodeData: GraphNode): void;
568
+ /**
569
+ * Select a link, automatically deselecting any current selection
570
+ */
571
+ selectLink(linkElement: SVGLineElement, renderableLink: RenderableGraphLink, event?: MouseEvent): void;
572
+ /**
573
+ * Deselect the currently selected node
574
+ */
575
+ private deselectNode;
576
+ /**
577
+ * Deselect the currently selected link
578
+ */
579
+ private deselectLink;
580
+ /**
581
+ * Clear all selections
582
+ */
583
+ clearSelection(): void;
584
+ /**
585
+ * Get current selection state
586
+ */
587
+ getSelectionState(): SelectionState;
588
+ /**
589
+ * Check if a specific node is selected
590
+ */
591
+ isNodeSelected(nodeData: GraphNode): boolean;
592
+ /**
593
+ * Check if a specific link is selected
594
+ */
595
+ isLinkSelected(linkData: GraphLink): boolean;
596
+ /**
597
+ * Handle click on background (deselect all)
598
+ */
599
+ handleBackgroundClick(event: MouseEvent, svg: SVGSVGElement, interactionRect: SVGRectElement): void;
600
+ /**
601
+ * Bring node and related elements to front using selection layer sub-layers
602
+ */
603
+ private bringNodeToFront;
604
+ /**
605
+ * Bring link and its label to front using selection layer
606
+ */
607
+ private bringLinkToFront;
608
+ /**
609
+ * Restore elements back to their original layers
610
+ */
611
+ private restoreSelectedElements;
612
+ /**
613
+ * Utility method to bring any SVG element to front using appendChild
614
+ * Based on the reference implementation pattern
615
+ */
616
+ private bringElementToFront;
617
+ /**
618
+ * Clear hover state to prevent conflicts with selection
619
+ * Similar to the clearAllHoverLayers function in create-node-hover.ts
620
+ */
621
+ private clearHoverState;
622
+ /**
623
+ * Clean up resources
624
+ */
625
+ destroy(): void;
626
+ }
627
+
628
+ export { type ErrorContext, ErrorHandler, type EventUnsubscribe, type GraphConfig, type GraphControlsConfig, GraphError, type GraphEventMap, type GraphEventName, type GraphInstance, type GraphInteractionConfig, type GraphLink, type GraphNode, GraphValidationError, GraphValidator, type LegendConfig, SelectionManager, type SelectionState, TypedGraphEventEmitter, type ValidationError, type ValidationResult, createGraph };