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,471 @@
1
+ // MintWaterfall Brush Selection System - TypeScript Version
2
+ // Provides interactive data selection with visual feedback and full type safety
3
+
4
+ import * as d3 from 'd3';
5
+
6
+ // Type definitions for brush system
7
+ export interface BrushConfig {
8
+ enabled: boolean;
9
+ extent: [[number, number], [number, number]];
10
+ handleSize: number;
11
+ filter: ((event: any) => boolean) | null;
12
+ touchable: boolean;
13
+ keyModifiers: boolean;
14
+ selection: {
15
+ fill: string;
16
+ fillOpacity: number;
17
+ stroke: string;
18
+ strokeWidth: number;
19
+ strokeDasharray: string | null;
20
+ };
21
+ handles: {
22
+ fill: string;
23
+ stroke: string;
24
+ strokeWidth: number;
25
+ size: number;
26
+ };
27
+ }
28
+
29
+ export interface BrushSelection {
30
+ x: [number, number];
31
+ y: [number, number];
32
+ }
33
+
34
+ export interface BrushEventData {
35
+ selection: BrushSelection | null;
36
+ sourceEvent: any;
37
+ type: 'start' | 'brush' | 'end';
38
+ }
39
+
40
+ export interface DataPoint {
41
+ x: number;
42
+ y: number;
43
+ [key: string]: any;
44
+ }
45
+
46
+ export interface BrushSystem {
47
+ enable(): BrushSystem;
48
+ disable(): BrushSystem;
49
+ attach(container: d3.Selection<SVGGElement, any, any, any>): BrushSystem;
50
+ detach(): BrushSystem;
51
+ clear(): BrushSystem;
52
+ getSelection(): BrushSelection | null;
53
+ setSelection(selection: BrushSelection | null): BrushSystem;
54
+ getSelectedData(): DataPoint[];
55
+ setData(data: DataPoint[]): BrushSystem;
56
+ configure(newConfig: Partial<BrushConfig>): BrushSystem;
57
+ setExtent(extent: [[number, number], [number, number]]): BrushSystem;
58
+ isEnabled(): boolean;
59
+ on(type: string, callback: (event: BrushEventData) => void): BrushSystem;
60
+ off(type: string, callback?: (event: BrushEventData) => void): BrushSystem;
61
+ }
62
+
63
+ export type BrushEventType = 'brushstart' | 'brush' | 'brushend' | 'clear';
64
+
65
+ export function createBrushSystem(): BrushSystem {
66
+
67
+ // Brush configuration
68
+ const config: BrushConfig = {
69
+ enabled: true,
70
+ extent: [[0, 0], [800, 400]],
71
+ handleSize: 6,
72
+ filter: null, // Use D3 default filter
73
+ touchable: true,
74
+ keyModifiers: true,
75
+ selection: {
76
+ fill: '#007acc',
77
+ fillOpacity: 0.3,
78
+ stroke: '#007acc',
79
+ strokeWidth: 1,
80
+ strokeDasharray: null
81
+ },
82
+ handles: {
83
+ fill: '#fff',
84
+ stroke: '#007acc',
85
+ strokeWidth: 1,
86
+ size: 6
87
+ }
88
+ };
89
+
90
+ let brushBehavior: d3.BrushBehavior<any> | null = null;
91
+ let currentSelection: BrushSelection | null = null;
92
+ let brushContainer: d3.Selection<SVGGElement, any, any, any> | null = null;
93
+ let dataPoints: DataPoint[] = [];
94
+
95
+ // Event listeners
96
+ const listeners = d3.dispatch("brushstart", "brush", "brushend", "clear");
97
+
98
+ function createBrushBehavior(): d3.BrushBehavior<any> {
99
+ if (brushBehavior) return brushBehavior;
100
+
101
+ brushBehavior = d3.brush<any>()
102
+ .extent(config.extent)
103
+ .handleSize(config.handleSize)
104
+ .touchable(config.touchable)
105
+ .keyModifiers(config.keyModifiers)
106
+ .on("start", handleBrushStart)
107
+ .on("brush", handleBrush)
108
+ .on("end", handleBrushEnd);
109
+
110
+ // Set filter if provided
111
+ if (config.filter) {
112
+ brushBehavior.filter(config.filter);
113
+ }
114
+
115
+ return brushBehavior;
116
+ }
117
+
118
+ function handleBrushStart(event: d3.D3BrushEvent<any>): void {
119
+ const selection = convertD3Selection(event.selection as [[number, number], [number, number]] | null);
120
+ currentSelection = selection;
121
+
122
+ const eventData: BrushEventData = {
123
+ selection,
124
+ sourceEvent: event.sourceEvent,
125
+ type: 'start'
126
+ };
127
+
128
+ listeners.call("brushstart", undefined, eventData);
129
+ }
130
+
131
+ function handleBrush(event: d3.D3BrushEvent<any>): void {
132
+ const selection = convertD3Selection(event.selection as [[number, number], [number, number]] | null);
133
+ currentSelection = selection;
134
+
135
+ const eventData: BrushEventData = {
136
+ selection,
137
+ sourceEvent: event.sourceEvent,
138
+ type: 'brush'
139
+ };
140
+
141
+ listeners.call("brush", undefined, eventData);
142
+ }
143
+
144
+ function handleBrushEnd(event: d3.D3BrushEvent<any>): void {
145
+ const selection = convertD3Selection(event.selection as [[number, number], [number, number]] | null);
146
+ currentSelection = selection;
147
+
148
+ const eventData: BrushEventData = {
149
+ selection,
150
+ sourceEvent: event.sourceEvent,
151
+ type: 'end'
152
+ };
153
+
154
+ listeners.call("brushend", undefined, eventData);
155
+ }
156
+
157
+ function convertD3Selection(d3Selection: [[number, number], [number, number]] | null): BrushSelection | null {
158
+ if (!d3Selection) return null;
159
+
160
+ const [[x0, y0], [x1, y1]] = d3Selection;
161
+ return {
162
+ x: [Math.min(x0, x1), Math.max(x0, x1)],
163
+ y: [Math.min(y0, y1), Math.max(y0, y1)]
164
+ };
165
+ }
166
+
167
+ function convertToBrushSelection(selection: BrushSelection): [[number, number], [number, number]] {
168
+ return [
169
+ [selection.x[0], selection.y[0]],
170
+ [selection.x[1], selection.y[1]]
171
+ ];
172
+ }
173
+
174
+ function applyBrushStyles(): void {
175
+ if (!brushContainer) return;
176
+
177
+ // Style the selection area
178
+ brushContainer.selectAll(".selection")
179
+ .style("fill", config.selection.fill)
180
+ .style("fill-opacity", config.selection.fillOpacity)
181
+ .style("stroke", config.selection.stroke)
182
+ .style("stroke-width", config.selection.strokeWidth);
183
+
184
+ // Apply stroke dash array only if it's not null
185
+ if (config.selection.strokeDasharray) {
186
+ brushContainer.selectAll(".selection")
187
+ .style("stroke-dasharray", config.selection.strokeDasharray);
188
+ }
189
+
190
+ // Style the handles
191
+ brushContainer.selectAll(".handle")
192
+ .style("fill", config.handles.fill)
193
+ .style("stroke", config.handles.stroke)
194
+ .style("stroke-width", config.handles.strokeWidth);
195
+ }
196
+
197
+ function filterDataBySelection(selection: BrushSelection): DataPoint[] {
198
+ if (!selection || dataPoints.length === 0) return [];
199
+
200
+ const [x0, x1] = selection.x;
201
+ const [y0, y1] = selection.y;
202
+
203
+ return dataPoints.filter(point => {
204
+ return point.x >= x0 && point.x <= x1 &&
205
+ point.y >= y0 && point.y <= y1;
206
+ });
207
+ }
208
+
209
+ // Enable brush
210
+ function enable(): BrushSystem {
211
+ config.enabled = true;
212
+ if (brushBehavior && brushContainer) {
213
+ brushContainer.call(brushBehavior);
214
+ applyBrushStyles();
215
+ }
216
+ return brushSystem;
217
+ }
218
+
219
+ // Disable brush
220
+ function disable(): BrushSystem {
221
+ config.enabled = false;
222
+ if (brushContainer) {
223
+ brushContainer.on(".brush", null);
224
+ }
225
+ return brushSystem;
226
+ }
227
+
228
+ // Attach brush to container
229
+ function attach(container: d3.Selection<SVGGElement, any, any, any>): BrushSystem {
230
+ brushContainer = container;
231
+
232
+ if (config.enabled) {
233
+ const behavior = createBrushBehavior();
234
+ container.call(behavior);
235
+ applyBrushStyles();
236
+ }
237
+
238
+ return brushSystem;
239
+ }
240
+
241
+ // Detach brush from container
242
+ function detach(): BrushSystem {
243
+ if (brushContainer) {
244
+ brushContainer.on(".brush", null);
245
+ brushContainer.selectAll(".brush").remove();
246
+ brushContainer = null;
247
+ }
248
+ return brushSystem;
249
+ }
250
+
251
+ // Clear current selection
252
+ function clear(): BrushSystem {
253
+ if (brushContainer && brushBehavior) {
254
+ brushContainer.call(brushBehavior.clear);
255
+ currentSelection = null;
256
+ listeners.call("clear", undefined, {
257
+ selection: null,
258
+ sourceEvent: null,
259
+ type: 'end'
260
+ });
261
+ }
262
+ return brushSystem;
263
+ }
264
+
265
+ // Get current selection
266
+ function getSelection(): BrushSelection | null {
267
+ return currentSelection;
268
+ }
269
+
270
+ // Set selection programmatically
271
+ function setSelection(selection: BrushSelection | null): BrushSystem {
272
+ if (brushContainer && brushBehavior) {
273
+ if (selection) {
274
+ const d3Selection = convertToBrushSelection(selection);
275
+ brushContainer.call(brushBehavior.move, d3Selection);
276
+ } else {
277
+ brushContainer.call(brushBehavior.clear);
278
+ }
279
+ currentSelection = selection;
280
+ }
281
+ return brushSystem;
282
+ }
283
+
284
+ // Get data points within current selection
285
+ function getSelectedData(): DataPoint[] {
286
+ if (!currentSelection) return [];
287
+ return filterDataBySelection(currentSelection);
288
+ }
289
+
290
+ // Set data points for selection filtering
291
+ function setData(data: DataPoint[]): BrushSystem {
292
+ dataPoints = [...data]; // Create a copy to avoid external mutations
293
+ return brushSystem;
294
+ }
295
+
296
+ // Configure brush system
297
+ function configure(newConfig: Partial<BrushConfig>): BrushSystem {
298
+ const oldExtent = config.extent;
299
+ Object.assign(config, newConfig);
300
+
301
+ // Update brush behavior if it exists
302
+ if (brushBehavior) {
303
+ if (newConfig.extent && newConfig.extent !== oldExtent) {
304
+ brushBehavior.extent(newConfig.extent);
305
+ }
306
+ if (newConfig.handleSize !== undefined) {
307
+ brushBehavior.handleSize(newConfig.handleSize);
308
+ }
309
+ if (newConfig.touchable !== undefined) {
310
+ brushBehavior.touchable(newConfig.touchable);
311
+ }
312
+ if (newConfig.keyModifiers !== undefined) {
313
+ brushBehavior.keyModifiers(newConfig.keyModifiers);
314
+ }
315
+ if (newConfig.filter !== undefined) {
316
+ if (newConfig.filter) {
317
+ brushBehavior.filter(newConfig.filter);
318
+ }
319
+ }
320
+ }
321
+
322
+ // Apply visual style updates
323
+ if (brushContainer) {
324
+ applyBrushStyles();
325
+ }
326
+
327
+ return brushSystem;
328
+ }
329
+
330
+ // Set brush extent
331
+ function setExtent(extent: [[number, number], [number, number]]): BrushSystem {
332
+ config.extent = extent;
333
+ if (brushBehavior) {
334
+ brushBehavior.extent(extent);
335
+ }
336
+ return brushSystem;
337
+ }
338
+
339
+ // Check if brush is enabled
340
+ function isEnabled(): boolean {
341
+ return config.enabled;
342
+ }
343
+
344
+ // Add event listener
345
+ function on(type: string, callback: (event: BrushEventData) => void): BrushSystem {
346
+ (listeners as any).on(type, callback);
347
+ return brushSystem;
348
+ }
349
+
350
+ // Remove event listener
351
+ function off(type: string, callback?: (event: BrushEventData) => void): BrushSystem {
352
+ (listeners as any).on(type, null);
353
+ return brushSystem;
354
+ }
355
+
356
+ const brushSystem: BrushSystem = {
357
+ enable,
358
+ disable,
359
+ attach,
360
+ detach,
361
+ clear,
362
+ getSelection,
363
+ setSelection,
364
+ getSelectedData,
365
+ setData,
366
+ configure,
367
+ setExtent,
368
+ isEnabled,
369
+ on,
370
+ off
371
+ };
372
+
373
+ return brushSystem;
374
+ }
375
+
376
+ // Factory function that returns the expected test API
377
+ export function createBrushSystemFactory() {
378
+ function createBrush(options: { type?: 'x' | 'y' | 'xy' } = {}): any {
379
+ const { type = 'xy' } = options;
380
+
381
+ switch (type) {
382
+ case 'x':
383
+ return d3.brushX();
384
+ case 'y':
385
+ return d3.brushY();
386
+ case 'xy':
387
+ default:
388
+ return d3.brush();
389
+ }
390
+ }
391
+
392
+ function filterDataByBrush(data: any[], selection: [number, number], scale: any): any[] {
393
+ if (!selection || !scale) return data;
394
+
395
+ const [start, end] = selection;
396
+ return data.filter(d => {
397
+ const value = scale(d.x || d.label || d.value);
398
+ return value >= start && value <= end;
399
+ });
400
+ }
401
+
402
+ function getSelectedIndices(data: any[], selection: [number, number], scale: any): number[] {
403
+ if (!selection || !scale) return [];
404
+
405
+ const [start, end] = selection;
406
+ const indices: number[] = [];
407
+
408
+ data.forEach((d, i) => {
409
+ const value = scale(d.x || d.label || d.value);
410
+ if (value >= start && value <= end) {
411
+ indices.push(i);
412
+ }
413
+ });
414
+
415
+ return indices;
416
+ }
417
+
418
+ const selectionUtils = {
419
+ createSelectionSummary(selectedData: any[]): any {
420
+ if (!selectedData || selectedData.length === 0) {
421
+ return {
422
+ count: 0,
423
+ sum: 0,
424
+ average: 0,
425
+ min: 0,
426
+ max: 0
427
+ };
428
+ }
429
+
430
+ const values = selectedData.map(d => d.cumulativeTotal || d.value || d.y || 0);
431
+ const sum = values.reduce((a, b) => a + b, 0);
432
+ const min = Math.min(...values);
433
+ const max = Math.max(...values);
434
+
435
+ return {
436
+ count: selectedData.length,
437
+ sum,
438
+ average: sum / selectedData.length,
439
+ min,
440
+ max,
441
+ extent: [min, max]
442
+ };
443
+ }
444
+ };
445
+
446
+ // Event handler methods
447
+ let startHandler: Function | null = null;
448
+ let moveHandler: Function | null = null;
449
+ let endHandler: Function | null = null;
450
+
451
+ const brushFactory = {
452
+ createBrush,
453
+ filterDataByBrush,
454
+ getSelectedIndices,
455
+ selectionUtils,
456
+ onStart(handler: Function) {
457
+ startHandler = handler;
458
+ return brushFactory;
459
+ },
460
+ onMove(handler: Function) {
461
+ moveHandler = handler;
462
+ return brushFactory;
463
+ },
464
+ onEnd(handler: Function) {
465
+ endHandler = handler;
466
+ return brushFactory;
467
+ }
468
+ };
469
+
470
+ return brushFactory;
471
+ }