mintwaterfall 0.8.6

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +223 -0
  2. package/CONTRIBUTING.md +199 -0
  3. package/README.md +363 -0
  4. package/dist/index.d.ts +149 -0
  5. package/dist/mintwaterfall.cjs.js +7978 -0
  6. package/dist/mintwaterfall.esm.js +7907 -0
  7. package/dist/mintwaterfall.min.js +7 -0
  8. package/dist/mintwaterfall.umd.js +7978 -0
  9. package/index.d.ts +149 -0
  10. package/package.json +126 -0
  11. package/src/enterprise/enterprise-core.js +0 -0
  12. package/src/enterprise/enterprise-feature-template.js +0 -0
  13. package/src/enterprise/feature-registry.js +0 -0
  14. package/src/enterprise/features/breakdown.js +0 -0
  15. package/src/features/breakdown.js +0 -0
  16. package/src/features/conditional-formatting.js +0 -0
  17. package/src/index.js +111 -0
  18. package/src/mintwaterfall-accessibility.ts +680 -0
  19. package/src/mintwaterfall-advanced-data.ts +1034 -0
  20. package/src/mintwaterfall-advanced-interactions.ts +649 -0
  21. package/src/mintwaterfall-advanced-performance.ts +582 -0
  22. package/src/mintwaterfall-animations.ts +595 -0
  23. package/src/mintwaterfall-brush.ts +471 -0
  24. package/src/mintwaterfall-chart-core.ts +296 -0
  25. package/src/mintwaterfall-chart.ts +1915 -0
  26. package/src/mintwaterfall-data.ts +1100 -0
  27. package/src/mintwaterfall-export.ts +475 -0
  28. package/src/mintwaterfall-hierarchical-layouts.ts +724 -0
  29. package/src/mintwaterfall-layouts.ts +647 -0
  30. package/src/mintwaterfall-performance.ts +573 -0
  31. package/src/mintwaterfall-scales.ts +437 -0
  32. package/src/mintwaterfall-shapes.ts +385 -0
  33. package/src/mintwaterfall-statistics.ts +821 -0
  34. package/src/mintwaterfall-themes.ts +391 -0
  35. package/src/mintwaterfall-tooltip.ts +450 -0
  36. package/src/mintwaterfall-zoom.ts +399 -0
  37. package/src/types/js-modules.d.ts +25 -0
  38. package/src/utils/compatibility-layer.js +0 -0
@@ -0,0 +1,582 @@
1
+ // MintWaterfall Advanced Performance Optimization - TypeScript Version
2
+ // Provides high-performance features for handling large datasets with D3.js optimization
3
+
4
+ import * as d3 from 'd3';
5
+
6
+ // ============================================================================
7
+ // TYPE DEFINITIONS
8
+ // ============================================================================
9
+
10
+ export interface QuadTreeNode {
11
+ x: number;
12
+ y: number;
13
+ data: any;
14
+ index: number;
15
+ }
16
+
17
+ export interface VirtualScrollConfig {
18
+ containerHeight: number;
19
+ itemHeight: number;
20
+ overscan: number; // Number of items to render outside visible area
21
+ threshold: number; // Minimum items before virtualization kicks in
22
+ }
23
+
24
+ export interface PerformanceMetrics {
25
+ renderTime: number;
26
+ dataProcessingTime: number;
27
+ memoryUsage: number;
28
+ frameRate: number;
29
+ itemsRendered: number;
30
+ totalItems: number;
31
+ virtualizationActive: boolean;
32
+ }
33
+
34
+ export interface SpatialIndex {
35
+ quadTree: d3.Quadtree<QuadTreeNode>;
36
+ search(x: number, y: number, radius?: number): QuadTreeNode[];
37
+ findNearest(x: number, y: number): QuadTreeNode | undefined;
38
+ add(node: QuadTreeNode): void;
39
+ remove(node: QuadTreeNode): void;
40
+ clear(): void;
41
+ size(): number;
42
+ }
43
+
44
+ export interface VirtualScrollManager {
45
+ getVisibleRange(scrollTop: number): { start: number; end: number };
46
+ getVirtualizedData<T>(data: T[], scrollTop: number): {
47
+ visibleData: T[];
48
+ offsetY: number;
49
+ totalHeight: number;
50
+ metrics: PerformanceMetrics;
51
+ };
52
+ updateConfig(config: Partial<VirtualScrollConfig>): void;
53
+ destroy(): void;
54
+ }
55
+
56
+ export interface CanvasRenderer {
57
+ render(data: any[], scales: any): void;
58
+ clear(): void;
59
+ getCanvas(): HTMLCanvasElement;
60
+ setDimensions(width: number, height: number): void;
61
+ enableHighDPI(): void;
62
+ }
63
+
64
+ export interface AdvancedPerformanceSystem {
65
+ // Spatial indexing
66
+ createSpatialIndex(): SpatialIndex;
67
+
68
+ // Virtual scrolling
69
+ createVirtualScrollManager(config: VirtualScrollConfig): VirtualScrollManager;
70
+
71
+ // Canvas rendering
72
+ createCanvasRenderer(container: HTMLElement): CanvasRenderer;
73
+
74
+ // Performance monitoring
75
+ createPerformanceMonitor(): PerformanceMonitor;
76
+
77
+ // Data optimization
78
+ optimizeDataForRendering<T>(data: T[], maxItems?: number): T[];
79
+ createDataSampler<T>(strategy: 'uniform' | 'random' | 'importance'): (data: T[], count: number) => T[];
80
+ }
81
+
82
+ export interface PerformanceMonitor {
83
+ startTiming(label: string): void;
84
+ endTiming(label: string): number;
85
+ getMetrics(): PerformanceMetrics;
86
+ getMemoryUsage(): number;
87
+ trackFrameRate(): void;
88
+ generateReport(): string;
89
+ }
90
+
91
+ // ============================================================================
92
+ // SPATIAL INDEXING IMPLEMENTATION
93
+ // ============================================================================
94
+
95
+ function createSpatialIndexImpl(): SpatialIndex {
96
+ let quadTree = d3.quadtree<QuadTreeNode>()
97
+ .x(d => d.x)
98
+ .y(d => d.y);
99
+
100
+ function search(x: number, y: number, radius: number = 10): QuadTreeNode[] {
101
+ const results: QuadTreeNode[] = [];
102
+
103
+ quadTree.visit((node, x1, y1, x2, y2) => {
104
+ if (!node.length) {
105
+ // Leaf node
106
+ const leaf = node as any;
107
+ if (leaf.data) {
108
+ const distance = Math.sqrt(
109
+ Math.pow(leaf.data.x - x, 2) + Math.pow(leaf.data.y - y, 2)
110
+ );
111
+ if (distance <= radius) {
112
+ results.push(leaf.data);
113
+ }
114
+ }
115
+ }
116
+ // Continue search if bounds intersect with search radius
117
+ return x1 > x + radius || y1 > y + radius || x2 < x - radius || y2 < y - radius;
118
+ });
119
+
120
+ return results;
121
+ }
122
+
123
+ function findNearest(x: number, y: number): QuadTreeNode | undefined {
124
+ return quadTree.find(x, y);
125
+ }
126
+
127
+ function add(node: QuadTreeNode): void {
128
+ quadTree.add(node);
129
+ }
130
+
131
+ function remove(node: QuadTreeNode): void {
132
+ quadTree.remove(node);
133
+ }
134
+
135
+ function clear(): void {
136
+ quadTree = d3.quadtree<QuadTreeNode>()
137
+ .x(d => d.x)
138
+ .y(d => d.y);
139
+ }
140
+
141
+ function size(): number {
142
+ let count = 0;
143
+ quadTree.visit(() => {
144
+ count++;
145
+ return false;
146
+ });
147
+ return count;
148
+ }
149
+
150
+ return {
151
+ quadTree,
152
+ search,
153
+ findNearest,
154
+ add,
155
+ remove,
156
+ clear,
157
+ size
158
+ };
159
+ }
160
+
161
+ // ============================================================================
162
+ // VIRTUAL SCROLLING IMPLEMENTATION
163
+ // ============================================================================
164
+
165
+ function createVirtualScrollManagerImpl(config: VirtualScrollConfig): VirtualScrollManager {
166
+ let currentConfig = { ...config };
167
+ let lastRenderTime = 0;
168
+ let frameCount = 0;
169
+ let frameRate = 60;
170
+
171
+ function getVisibleRange(scrollTop: number): { start: number; end: number } {
172
+ const visibleStart = Math.floor(scrollTop / currentConfig.itemHeight);
173
+ const visibleEnd = Math.ceil(
174
+ (scrollTop + currentConfig.containerHeight) / currentConfig.itemHeight
175
+ );
176
+
177
+ // Add overscan
178
+ const start = Math.max(0, visibleStart - currentConfig.overscan);
179
+ const end = visibleEnd + currentConfig.overscan;
180
+
181
+ return { start, end };
182
+ }
183
+
184
+ function getVirtualizedData<T>(data: T[], scrollTop: number): {
185
+ visibleData: T[];
186
+ offsetY: number;
187
+ totalHeight: number;
188
+ metrics: PerformanceMetrics;
189
+ } {
190
+ const startTime = performance.now();
191
+
192
+ // Check if virtualization should be active
193
+ const virtualizationActive = data.length >= currentConfig.threshold;
194
+
195
+ if (!virtualizationActive) {
196
+ const endTime = performance.now();
197
+ return {
198
+ visibleData: data,
199
+ offsetY: 0,
200
+ totalHeight: data.length * currentConfig.itemHeight,
201
+ metrics: {
202
+ renderTime: endTime - startTime,
203
+ dataProcessingTime: endTime - startTime,
204
+ memoryUsage: getMemoryUsage(),
205
+ frameRate,
206
+ itemsRendered: data.length,
207
+ totalItems: data.length,
208
+ virtualizationActive: false
209
+ }
210
+ };
211
+ }
212
+
213
+ const { start, end } = getVisibleRange(scrollTop);
214
+ const visibleData = data.slice(start, Math.min(end, data.length));
215
+ const offsetY = start * currentConfig.itemHeight;
216
+ const totalHeight = data.length * currentConfig.itemHeight;
217
+
218
+ const endTime = performance.now();
219
+
220
+ // Update frame rate calculation
221
+ frameCount++;
222
+ if (frameCount % 60 === 0) {
223
+ const now = performance.now();
224
+ if (lastRenderTime > 0) {
225
+ frameRate = 60000 / (now - lastRenderTime);
226
+ }
227
+ lastRenderTime = now;
228
+ }
229
+
230
+ return {
231
+ visibleData,
232
+ offsetY,
233
+ totalHeight,
234
+ metrics: {
235
+ renderTime: endTime - startTime,
236
+ dataProcessingTime: endTime - startTime,
237
+ memoryUsage: getMemoryUsage(),
238
+ frameRate,
239
+ itemsRendered: visibleData.length,
240
+ totalItems: data.length,
241
+ virtualizationActive: true
242
+ }
243
+ };
244
+ }
245
+
246
+ function updateConfig(newConfig: Partial<VirtualScrollConfig>): void {
247
+ currentConfig = { ...currentConfig, ...newConfig };
248
+ }
249
+
250
+ function destroy(): void {
251
+ // Cleanup if needed
252
+ }
253
+
254
+ return {
255
+ getVisibleRange,
256
+ getVirtualizedData,
257
+ updateConfig,
258
+ destroy
259
+ };
260
+ }
261
+
262
+ // ============================================================================
263
+ // CANVAS RENDERING IMPLEMENTATION
264
+ // ============================================================================
265
+
266
+ function createCanvasRendererImpl(container: HTMLElement): CanvasRenderer {
267
+ const canvas = document.createElement('canvas');
268
+ const context = canvas.getContext('2d')!;
269
+
270
+ container.appendChild(canvas);
271
+
272
+ function render(data: any[], scales: any): void {
273
+ const { xScale, yScale } = scales;
274
+
275
+ // Clear canvas
276
+ context.clearRect(0, 0, canvas.width, canvas.height);
277
+
278
+ // Set drawing properties for better performance
279
+ context.save();
280
+
281
+ // Render data points efficiently
282
+ data.forEach((item, index) => {
283
+ const x = xScale(item.label) || 0;
284
+ const y = yScale(item.value) || 0;
285
+ const width = xScale.bandwidth ? xScale.bandwidth() : 20;
286
+ const height = Math.abs(yScale(0) - y);
287
+
288
+ // Use fillRect for better performance than path operations
289
+ context.fillStyle = item.color || '#3498db';
290
+ context.fillRect(x, Math.min(y, yScale(0)), width, height);
291
+
292
+ // Add border if needed
293
+ context.strokeStyle = '#ffffff';
294
+ context.lineWidth = 1;
295
+ context.strokeRect(x, Math.min(y, yScale(0)), width, height);
296
+ });
297
+
298
+ context.restore();
299
+ }
300
+
301
+ function clear(): void {
302
+ context.clearRect(0, 0, canvas.width, canvas.height);
303
+ }
304
+
305
+ function getCanvas(): HTMLCanvasElement {
306
+ return canvas;
307
+ }
308
+
309
+ function setDimensions(width: number, height: number): void {
310
+ canvas.width = width;
311
+ canvas.height = height;
312
+ canvas.style.width = `${width}px`;
313
+ canvas.style.height = `${height}px`;
314
+ }
315
+
316
+ function enableHighDPI(): void {
317
+ const dpr = window.devicePixelRatio || 1;
318
+ const rect = canvas.getBoundingClientRect();
319
+
320
+ canvas.width = rect.width * dpr;
321
+ canvas.height = rect.height * dpr;
322
+ canvas.style.width = `${rect.width}px`;
323
+ canvas.style.height = `${rect.height}px`;
324
+
325
+ context.scale(dpr, dpr);
326
+ }
327
+
328
+ return {
329
+ render,
330
+ clear,
331
+ getCanvas,
332
+ setDimensions,
333
+ enableHighDPI
334
+ };
335
+ }
336
+
337
+ // ============================================================================
338
+ // PERFORMANCE MONITORING IMPLEMENTATION
339
+ // ============================================================================
340
+
341
+ function createPerformanceMonitorImpl(): PerformanceMonitor {
342
+ const timings: Map<string, number> = new Map();
343
+ const completed: Map<string, number[]> = new Map();
344
+ let frameCount = 0;
345
+ let frameStartTime = performance.now();
346
+
347
+ function startTiming(label: string): void {
348
+ timings.set(label, performance.now());
349
+ }
350
+
351
+ function endTiming(label: string): number {
352
+ const startTime = timings.get(label);
353
+ if (!startTime) {
354
+ console.warn(`No start time found for timing label: ${label}`);
355
+ return 0;
356
+ }
357
+
358
+ const duration = performance.now() - startTime;
359
+
360
+ // Store completed timing
361
+ if (!completed.has(label)) {
362
+ completed.set(label, []);
363
+ }
364
+ completed.get(label)!.push(duration);
365
+
366
+ // Keep only last 100 measurements
367
+ const measurements = completed.get(label)!;
368
+ if (measurements.length > 100) {
369
+ measurements.shift();
370
+ }
371
+
372
+ timings.delete(label);
373
+ return duration;
374
+ }
375
+
376
+ function getMetrics(): PerformanceMetrics {
377
+ const renderTimes = completed.get('render') || [];
378
+ const processingTimes = completed.get('dataProcessing') || [];
379
+
380
+ return {
381
+ renderTime: renderTimes.length > 0 ? d3.mean(renderTimes) || 0 : 0,
382
+ dataProcessingTime: processingTimes.length > 0 ? d3.mean(processingTimes) || 0 : 0,
383
+ memoryUsage: getMemoryUsage(),
384
+ frameRate: frameCount > 0 ? 1000 / ((performance.now() - frameStartTime) / frameCount) : 0,
385
+ itemsRendered: 0, // To be set by caller
386
+ totalItems: 0, // To be set by caller
387
+ virtualizationActive: false // To be set by caller
388
+ };
389
+ }
390
+
391
+ function trackFrameRate(): void {
392
+ frameCount++;
393
+ if (frameCount === 1) {
394
+ frameStartTime = performance.now();
395
+ }
396
+ }
397
+
398
+ function generateReport(): string {
399
+ const metrics = getMetrics();
400
+ return `
401
+ Performance Report:
402
+ - Average Render Time: ${metrics.renderTime.toFixed(2)}ms
403
+ - Average Processing Time: ${metrics.dataProcessingTime.toFixed(2)}ms
404
+ - Memory Usage: ${metrics.memoryUsage.toFixed(2)}MB
405
+ - Frame Rate: ${metrics.frameRate.toFixed(1)}fps
406
+ - Items Rendered: ${metrics.itemsRendered}/${metrics.totalItems}
407
+ - Virtualization: ${metrics.virtualizationActive ? 'Active' : 'Inactive'}
408
+ `.trim();
409
+ }
410
+
411
+ return {
412
+ startTiming,
413
+ endTiming,
414
+ getMetrics,
415
+ getMemoryUsage,
416
+ trackFrameRate,
417
+ generateReport
418
+ };
419
+ }
420
+
421
+ // ============================================================================
422
+ // UTILITY FUNCTIONS
423
+ // ============================================================================
424
+
425
+ function getMemoryUsage(): number {
426
+ if ('memory' in performance) {
427
+ const memory = (performance as any).memory;
428
+ return memory.usedJSHeapSize / (1024 * 1024); // Convert to MB
429
+ }
430
+ return 0; // Memory API not available
431
+ }
432
+
433
+ // ============================================================================
434
+ // DATA OPTIMIZATION FUNCTIONS
435
+ // ============================================================================
436
+
437
+ function optimizeDataForRenderingImpl<T>(data: T[], maxItems: number = 1000): T[] {
438
+ if (data.length <= maxItems) {
439
+ return data;
440
+ }
441
+
442
+ // Use uniform sampling to reduce data points while maintaining distribution
443
+ const step = Math.ceil(data.length / maxItems);
444
+ const optimized: T[] = [];
445
+
446
+ for (let i = 0; i < data.length; i += step) {
447
+ optimized.push(data[i]);
448
+ }
449
+
450
+ return optimized;
451
+ }
452
+
453
+ function createDataSamplerImpl<T>(strategy: 'uniform' | 'random' | 'importance'): (data: T[], count: number) => T[] {
454
+ switch (strategy) {
455
+ case 'uniform':
456
+ return (data: T[], count: number): T[] => {
457
+ if (count >= data.length) return data;
458
+ const step = data.length / count;
459
+ const result: T[] = [];
460
+ for (let i = 0; i < count; i++) {
461
+ const index = Math.floor(i * step);
462
+ result.push(data[index]);
463
+ }
464
+ return result;
465
+ };
466
+
467
+ case 'random':
468
+ return (data: T[], count: number): T[] => {
469
+ if (count >= data.length) return data;
470
+ const shuffled = [...data].sort(() => 0.5 - Math.random());
471
+ return shuffled.slice(0, count);
472
+ };
473
+
474
+ case 'importance':
475
+ return (data: T[], count: number): T[] => {
476
+ if (count >= data.length) return data;
477
+ // For importance sampling, we'd need a way to assess importance
478
+ // For now, take first and last items plus uniform sampling in between
479
+ const result: T[] = [data[0]]; // Always include first
480
+
481
+ if (count > 2) {
482
+ const middle = createDataSamplerImpl<T>('uniform')(
483
+ data.slice(1, -1),
484
+ count - 2
485
+ );
486
+ result.push(...middle);
487
+ }
488
+
489
+ if (count > 1) {
490
+ result.push(data[data.length - 1]); // Always include last
491
+ }
492
+
493
+ return result;
494
+ };
495
+
496
+ default:
497
+ throw new Error(`Unknown sampling strategy: ${strategy}`);
498
+ }
499
+ }
500
+
501
+ // ============================================================================
502
+ // MAIN SYSTEM IMPLEMENTATION
503
+ // ============================================================================
504
+
505
+ export function createAdvancedPerformanceSystem(): AdvancedPerformanceSystem {
506
+ return {
507
+ createSpatialIndex: createSpatialIndexImpl,
508
+ createVirtualScrollManager: createVirtualScrollManagerImpl,
509
+ createCanvasRenderer: createCanvasRendererImpl,
510
+ createPerformanceMonitor: createPerformanceMonitorImpl,
511
+ optimizeDataForRendering: optimizeDataForRenderingImpl,
512
+ createDataSampler: createDataSamplerImpl
513
+ };
514
+ }
515
+
516
+ // ============================================================================
517
+ // WATERFALL-SPECIFIC PERFORMANCE UTILITIES
518
+ // ============================================================================
519
+
520
+ /**
521
+ * Create optimized spatial index for waterfall chart interactions
522
+ * Enables O(log n) hover detection for large datasets
523
+ */
524
+ export function createWaterfallSpatialIndex(
525
+ data: Array<{label: string, value: number}>,
526
+ xScale: any,
527
+ yScale: any
528
+ ): SpatialIndex {
529
+ const spatialIndex = createSpatialIndexImpl();
530
+
531
+ data.forEach((item, index) => {
532
+ const x = (xScale(item.label) || 0) + (xScale.bandwidth ? xScale.bandwidth() / 2 : 0);
533
+ const y = yScale(item.value) || 0;
534
+
535
+ spatialIndex.add({
536
+ x,
537
+ y,
538
+ data: item,
539
+ index
540
+ });
541
+ });
542
+
543
+ return spatialIndex;
544
+ }
545
+
546
+ /**
547
+ * Create high-performance virtual waterfall renderer
548
+ * Handles thousands of data points with smooth scrolling
549
+ */
550
+ export function createVirtualWaterfallRenderer(
551
+ container: HTMLElement,
552
+ config: VirtualScrollConfig
553
+ ): {
554
+ virtualScrollManager: VirtualScrollManager;
555
+ performanceMonitor: PerformanceMonitor;
556
+ render: (data: any[], scrollTop: number) => void;
557
+ } {
558
+ const system = createAdvancedPerformanceSystem();
559
+ const virtualScrollManager = system.createVirtualScrollManager(config);
560
+ const performanceMonitor = system.createPerformanceMonitor();
561
+
562
+ function render(data: any[], scrollTop: number): void {
563
+ performanceMonitor.startTiming('render');
564
+
565
+ const virtualized = virtualScrollManager.getVirtualizedData(data, scrollTop);
566
+
567
+ // Here you would render only the visible data
568
+ // This is a simplified version - in practice, you'd integrate with D3.js rendering
569
+
570
+ performanceMonitor.endTiming('render');
571
+ performanceMonitor.trackFrameRate();
572
+ }
573
+
574
+ return {
575
+ virtualScrollManager,
576
+ performanceMonitor,
577
+ render
578
+ };
579
+ }
580
+
581
+ // Default export for convenience
582
+ export default createAdvancedPerformanceSystem;