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,450 @@
1
+ // MintWaterfall Professional Tooltip System - TypeScript Version
2
+ // Provides intelligent positioning, rich content, and customizable styling with full type safety
3
+
4
+ import * as d3 from 'd3';
5
+
6
+ // Type definitions for tooltip system
7
+ export interface TooltipOffset {
8
+ x: number;
9
+ y: number;
10
+ }
11
+
12
+ export interface TooltipAnimation {
13
+ duration: number;
14
+ easing: string;
15
+ }
16
+
17
+ export interface TooltipCollision {
18
+ boundary: 'viewport' | 'container';
19
+ flip: boolean;
20
+ shift: boolean;
21
+ }
22
+
23
+ export interface TooltipContent {
24
+ maxWidth: number;
25
+ padding: number;
26
+ }
27
+
28
+ export interface TooltipConfig {
29
+ className: string;
30
+ theme: TooltipTheme;
31
+ position: TooltipPosition;
32
+ offset: TooltipOffset;
33
+ animation: TooltipAnimation;
34
+ collision: TooltipCollision;
35
+ content: TooltipContent;
36
+ formatNumber?: (n: number) => string;
37
+ }
38
+
39
+ export interface TooltipThemeStyles {
40
+ background: string;
41
+ color: string;
42
+ border: string;
43
+ borderRadius: string;
44
+ fontSize: string;
45
+ fontFamily: string;
46
+ boxShadow: string;
47
+ maxWidth: string;
48
+ padding: string;
49
+ }
50
+
51
+ export interface TooltipTemplateConfig {
52
+ template: string;
53
+ formatters?: { [key: string]: (value: any) => string };
54
+ }
55
+
56
+ export interface TooltipData {
57
+ label: string;
58
+ stacks?: Array<{
59
+ value: number;
60
+ color?: string;
61
+ label?: string;
62
+ }>;
63
+ [key: string]: any;
64
+ }
65
+
66
+ export interface TooltipStackItem {
67
+ value: number;
68
+ color?: string;
69
+ label?: string;
70
+ }
71
+
72
+ export interface CurrentTooltip {
73
+ content: TooltipContentType;
74
+ event: MouseEvent | PointerEvent | TouchEvent;
75
+ data: TooltipData | null;
76
+ }
77
+
78
+ export interface TooltipPosition3D {
79
+ x: number;
80
+ y: number;
81
+ quadrant: number;
82
+ }
83
+
84
+ export interface TooltipSystem {
85
+ show(content: TooltipContentType, event: MouseEvent | PointerEvent | TouchEvent, data?: TooltipData | null): d3.Selection<HTMLDivElement, unknown, HTMLElement, any>;
86
+ hide(): d3.Selection<HTMLDivElement, unknown, HTMLElement, any> | void;
87
+ move(event: MouseEvent | PointerEvent | TouchEvent): d3.Selection<HTMLDivElement, unknown, HTMLElement, any> | void;
88
+ theme(themeName: TooltipTheme): TooltipSystem;
89
+ configure(newConfig: Partial<TooltipConfig>): TooltipSystem;
90
+ destroy(): void;
91
+ isVisible(): boolean;
92
+ getCurrentData(): TooltipData | null;
93
+ }
94
+
95
+ export type TooltipTheme = 'default' | 'light' | 'minimal' | 'corporate';
96
+ export type TooltipPosition = 'smart' | 'top' | 'bottom' | 'left' | 'right' | 'follow';
97
+ export type TooltipContentType = string | TooltipTemplateConfig | ((data: TooltipData | null) => string);
98
+
99
+ export function createTooltipSystem(): TooltipSystem {
100
+
101
+ let tooltipContainer: d3.Selection<HTMLDivElement, unknown, HTMLElement, any> | null = null;
102
+ let currentTooltip: CurrentTooltip | null = null;
103
+ let config: TooltipConfig = {
104
+ className: "mintwaterfall-tooltip",
105
+ theme: "default",
106
+ position: "smart",
107
+ offset: { x: 10, y: -10 },
108
+ animation: {
109
+ duration: 200,
110
+ easing: "ease-out"
111
+ },
112
+ collision: {
113
+ boundary: "viewport",
114
+ flip: true,
115
+ shift: true
116
+ },
117
+ content: {
118
+ maxWidth: 300,
119
+ padding: 12
120
+ }
121
+ };
122
+
123
+ // Initialize tooltip container
124
+ function initializeTooltip(): d3.Selection<HTMLDivElement, unknown, HTMLElement, any> {
125
+ if (tooltipContainer) return tooltipContainer;
126
+
127
+ tooltipContainer = d3.select("body")
128
+ .append("div")
129
+ .attr("class", config.className)
130
+ .style("position", "absolute")
131
+ .style("visibility", "hidden")
132
+ .style("pointer-events", "none")
133
+ .style("z-index", "9999")
134
+ .style("opacity", "0")
135
+ .style("transition", `opacity ${config.animation.duration}ms ${config.animation.easing}`) as d3.Selection<HTMLDivElement, unknown, HTMLElement, any>;
136
+
137
+ applyTheme(config.theme);
138
+
139
+ return tooltipContainer;
140
+ }
141
+
142
+ // Apply tooltip theme
143
+ function applyTheme(themeName: TooltipTheme): void {
144
+ if (!tooltipContainer) return;
145
+
146
+ const themes: Record<TooltipTheme, TooltipThemeStyles> = {
147
+ default: {
148
+ background: "rgba(0, 0, 0, 0.9)",
149
+ color: "#ffffff",
150
+ border: "none",
151
+ borderRadius: "6px",
152
+ fontSize: "13px",
153
+ fontFamily: "system-ui, -apple-system, sans-serif",
154
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.3)",
155
+ maxWidth: `${config.content.maxWidth}px`,
156
+ padding: `${config.content.padding}px`
157
+ },
158
+ light: {
159
+ background: "rgba(255, 255, 255, 0.95)",
160
+ color: "#333333",
161
+ border: "1px solid rgba(0, 0, 0, 0.1)",
162
+ borderRadius: "6px",
163
+ fontSize: "13px",
164
+ fontFamily: "system-ui, -apple-system, sans-serif",
165
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
166
+ maxWidth: `${config.content.maxWidth}px`,
167
+ padding: `${config.content.padding}px`
168
+ },
169
+ minimal: {
170
+ background: "#333333",
171
+ color: "#ffffff",
172
+ border: "none",
173
+ borderRadius: "3px",
174
+ fontSize: "12px",
175
+ fontFamily: "monospace",
176
+ boxShadow: "none",
177
+ maxWidth: `${config.content.maxWidth}px`,
178
+ padding: "8px 10px"
179
+ },
180
+ corporate: {
181
+ background: "#2c3e50",
182
+ color: "#ecf0f1",
183
+ border: "1px solid #34495e",
184
+ borderRadius: "4px",
185
+ fontSize: "13px",
186
+ fontFamily: "system-ui, -apple-system, sans-serif",
187
+ boxShadow: "0 2px 8px rgba(0, 0, 0, 0.2)",
188
+ maxWidth: `${config.content.maxWidth}px`,
189
+ padding: `${config.content.padding}px`
190
+ }
191
+ };
192
+
193
+ const theme = themes[themeName] || themes.default;
194
+
195
+ Object.keys(theme).forEach((property: string) => {
196
+ const cssProperty = property.replace(/([A-Z])/g, "-$1").toLowerCase();
197
+ const value = theme[property as keyof TooltipThemeStyles];
198
+ tooltipContainer!.style(cssProperty, value);
199
+ });
200
+ }
201
+
202
+ // Show tooltip with content
203
+ function show(content: TooltipContentType, event: MouseEvent | PointerEvent | TouchEvent, data: TooltipData | null = null): d3.Selection<HTMLDivElement, unknown, HTMLElement, any> {
204
+ if (!tooltipContainer) initializeTooltip();
205
+
206
+ // Generate content
207
+ const htmlContent = generateContent(content, data);
208
+
209
+ tooltipContainer!
210
+ .html(htmlContent)
211
+ .style("visibility", "visible");
212
+
213
+ // Position tooltip
214
+ positionTooltip(event);
215
+
216
+ // Animate in
217
+ tooltipContainer!
218
+ .transition()
219
+ .duration(config.animation.duration)
220
+ .style("opacity", "1");
221
+
222
+ currentTooltip = { content, event, data };
223
+
224
+ return tooltipContainer!;
225
+ }
226
+
227
+ // Hide tooltip
228
+ function hide(): d3.Selection<HTMLDivElement, unknown, HTMLElement, any> | void {
229
+ if (!tooltipContainer) return;
230
+
231
+ tooltipContainer
232
+ .transition()
233
+ .duration(config.animation.duration)
234
+ .style("opacity", "0")
235
+ .on("end", function() {
236
+ d3.select(this).style("visibility", "hidden");
237
+ });
238
+
239
+ currentTooltip = null;
240
+
241
+ return tooltipContainer;
242
+ }
243
+
244
+ // Update tooltip position
245
+ function move(event: MouseEvent | PointerEvent | TouchEvent): d3.Selection<HTMLDivElement, unknown, HTMLElement, any> | void {
246
+ if (!tooltipContainer || !currentTooltip) return;
247
+
248
+ positionTooltip(event);
249
+ return tooltipContainer;
250
+ }
251
+
252
+ // Generate tooltip content
253
+ function generateContent(content: TooltipContentType, data: TooltipData | null): string {
254
+ if (typeof content === "function") {
255
+ return content(data);
256
+ }
257
+
258
+ if (typeof content === "string") {
259
+ return content;
260
+ }
261
+
262
+ if (typeof content === "object" && content && 'template' in content) {
263
+ return renderTemplate(content.template, data, content.formatters);
264
+ }
265
+
266
+ // Default content for waterfall chart data
267
+ if (data) {
268
+ return generateDefaultContent(data);
269
+ }
270
+
271
+ return "";
272
+ }
273
+
274
+ // Generate default content for chart data
275
+ function generateDefaultContent(data: TooltipData): string {
276
+ const formatNumber = config.formatNumber || ((n: number) => n.toLocaleString());
277
+
278
+ let html = `<div class="tooltip-header"><strong>${data.label}</strong></div>`;
279
+
280
+ if (data.stacks && data.stacks.length > 0) {
281
+ const totalValue = data.stacks.reduce((sum, stack) => sum + stack.value, 0);
282
+
283
+ html += `<div class="tooltip-total">Total: ${formatNumber(totalValue)}</div>`;
284
+
285
+ if (data.stacks.length > 1) {
286
+ html += "<div class=\"tooltip-stacks\">";
287
+ data.stacks.forEach(stack => {
288
+ const color = stack.color || "#666";
289
+ const label = stack.label || formatNumber(stack.value);
290
+ html += `
291
+ <div class="tooltip-stack-item">
292
+ <span class="tooltip-color-indicator" style="background-color: ${color}"></span>
293
+ <span class="tooltip-stack-label">${label}</span>
294
+ <span class="tooltip-stack-value">${formatNumber(stack.value)}</span>
295
+ </div>
296
+ `;
297
+ });
298
+ html += "</div>";
299
+ }
300
+ }
301
+
302
+ return html;
303
+ }
304
+
305
+ // Render template with data
306
+ function renderTemplate(template: string, data: TooltipData | null, formatters: { [key: string]: (value: any) => string } = {}): string {
307
+ if (!data) return template;
308
+
309
+ let rendered = template;
310
+
311
+ // Replace placeholders like {{key}} with data values
312
+ rendered = rendered.replace(/\{\{(\w+(?:\.\w+)*)\}\}/g, (match, key) => {
313
+ const value = getNestedValue(data, key);
314
+ const formatter = formatters[key];
315
+
316
+ if (formatter && typeof formatter === 'function') {
317
+ return formatter(value);
318
+ }
319
+
320
+ return value != null ? String(value) : '';
321
+ });
322
+
323
+ return rendered;
324
+ }
325
+
326
+ // Get nested value from object using dot notation
327
+ function getNestedValue(obj: any, path: string): any {
328
+ return path.split('.').reduce((current, key) => current?.[key], obj);
329
+ }
330
+
331
+ // Position tooltip intelligently
332
+ function positionTooltip(event: MouseEvent | PointerEvent | TouchEvent): void {
333
+ if (!tooltipContainer) return;
334
+
335
+ const mouseEvent = event as MouseEvent;
336
+ const tooltipNode = tooltipContainer.node();
337
+ if (!tooltipNode) return;
338
+
339
+ const tooltipRect = tooltipNode.getBoundingClientRect();
340
+ const viewportWidth = window.innerWidth;
341
+ const viewportHeight = window.innerHeight;
342
+
343
+ let x = mouseEvent.pageX + config.offset.x;
344
+ let y = mouseEvent.pageY + config.offset.y;
345
+
346
+ // Smart positioning to avoid viewport edges
347
+ if (config.position === "smart") {
348
+ const position = calculateSmartPosition(
349
+ { x: mouseEvent.clientX, y: mouseEvent.clientY },
350
+ { width: tooltipRect.width, height: tooltipRect.height },
351
+ { width: viewportWidth, height: viewportHeight }
352
+ );
353
+
354
+ x = position.x + window.pageXOffset;
355
+ y = position.y + window.pageYOffset;
356
+ }
357
+
358
+ tooltipContainer
359
+ .style("left", `${x}px`)
360
+ .style("top", `${y}px`);
361
+ }
362
+
363
+ // Calculate smart position to avoid clipping
364
+ function calculateSmartPosition(
365
+ mouse: { x: number; y: number },
366
+ tooltip: { width: number; height: number },
367
+ viewport: { width: number; height: number }
368
+ ): TooltipPosition3D {
369
+ const padding = 10;
370
+ let x = mouse.x + config.offset.x;
371
+ let y = mouse.y + config.offset.y;
372
+ let quadrant = 1;
373
+
374
+ // Check right edge
375
+ if (x + tooltip.width + padding > viewport.width) {
376
+ x = mouse.x - tooltip.width - Math.abs(config.offset.x);
377
+ quadrant = 2;
378
+ }
379
+
380
+ // Check bottom edge
381
+ if (y + tooltip.height + padding > viewport.height) {
382
+ y = mouse.y - tooltip.height - Math.abs(config.offset.y);
383
+ quadrant = quadrant === 2 ? 3 : 4;
384
+ }
385
+
386
+ // Check left edge
387
+ if (x < padding) {
388
+ x = padding;
389
+ }
390
+
391
+ // Check top edge
392
+ if (y < padding) {
393
+ y = padding;
394
+ }
395
+
396
+ return { x, y, quadrant };
397
+ }
398
+
399
+ // Configure tooltip
400
+ function configure(newConfig: Partial<TooltipConfig>): TooltipSystem {
401
+ config = { ...config, ...newConfig };
402
+
403
+ if (tooltipContainer && newConfig.theme) {
404
+ applyTheme(newConfig.theme);
405
+ }
406
+
407
+ return tooltipSystem;
408
+ }
409
+
410
+ // Set theme
411
+ function theme(themeName: TooltipTheme): TooltipSystem {
412
+ config.theme = themeName;
413
+ if (tooltipContainer) {
414
+ applyTheme(themeName);
415
+ }
416
+ return tooltipSystem;
417
+ }
418
+
419
+ // Destroy tooltip
420
+ function destroy(): void {
421
+ if (tooltipContainer) {
422
+ tooltipContainer.remove();
423
+ tooltipContainer = null;
424
+ }
425
+ currentTooltip = null;
426
+ }
427
+
428
+ // Check if tooltip is visible
429
+ function isVisible(): boolean {
430
+ return tooltipContainer !== null && tooltipContainer.style("visibility") === "visible";
431
+ }
432
+
433
+ // Get current tooltip data
434
+ function getCurrentData(): TooltipData | null {
435
+ return currentTooltip?.data || null;
436
+ }
437
+
438
+ const tooltipSystem: TooltipSystem = {
439
+ show,
440
+ hide,
441
+ move,
442
+ theme,
443
+ configure,
444
+ destroy,
445
+ isVisible,
446
+ getCurrentData
447
+ };
448
+
449
+ return tooltipSystem;
450
+ }