flikkui 0.1.0-beta.3 → 0.1.0-beta.5
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/README.md +155 -0
- package/dist/components/charts/AreaChart/AreaChart.js +434 -0
- package/dist/components/charts/AreaChart/AreaChart.types.js +7 -0
- package/dist/components/charts/BarChart/BarChart.js +402 -0
- package/dist/components/charts/BarChart/BarChart.types.js +7 -0
- package/dist/components/charts/ChartContainer.js +38 -0
- package/dist/components/charts/Heatmap/Heatmap.js +153 -0
- package/dist/components/charts/Heatmap/HeatmapCell.js +100 -0
- package/dist/components/charts/Heatmap/HeatmapLegend.js +20 -0
- package/dist/components/charts/Heatmap/utils/heatmapUtils.js +174 -0
- package/dist/components/charts/LineChart/LineChart.js +396 -0
- package/dist/components/charts/LineChart/LineChart.types.js +7 -0
- package/dist/components/charts/hooks/useChartAccessibility.js +127 -0
- package/dist/components/charts/hooks/useChartTheme.js +86 -0
- package/dist/components/charts/hooks/useChartValidation.js +59 -0
- package/dist/components/charts/hooks/useTooltipPosition.js +292 -0
- package/dist/components/charts/shared/ChartAxis/XAxis.js +30 -0
- package/dist/components/charts/shared/ChartAxis/YAxis.js +97 -0
- package/dist/components/charts/shared/ChartCrosshair/ChartCrosshair.js +35 -0
- package/dist/components/charts/shared/ChartCrosshair/ChartCrosshair.theme.js +11 -0
- package/dist/components/charts/shared/ChartErrorBoundary/ChartErrorBoundary.js +66 -0
- package/dist/components/charts/shared/ChartGrid/HorizontalGrid.js +22 -0
- package/dist/components/charts/shared/ChartLegend/ChartLegend.js +30 -0
- package/dist/components/charts/shared/ChartLegend/ChartLegendContent.js +22 -0
- package/dist/components/charts/shared/ChartMarker/ChartMarker.js +25 -0
- package/dist/components/charts/shared/ChartMarker/ChartMarker.theme.js +9 -0
- package/dist/components/charts/shared/ChartText/ChartText.js +33 -0
- package/dist/components/charts/shared/ChartText/ChartText.theme.js +9 -0
- package/dist/components/charts/shared/ChartTooltip/ChartTooltip.js +62 -0
- package/dist/components/charts/theme/chart.theme.js +73 -0
- package/dist/components/charts/types/chart.types.js +29 -0
- package/dist/components/charts/utils/chart-validation.js +292 -0
- package/dist/components/charts/utils/color-utils.js +175 -0
- package/dist/components/core/Accordion/Accordion.animations.js +45 -0
- package/dist/components/core/Accordion/Accordion.js +52 -0
- package/dist/components/core/Accordion/Accordion.theme.js +8 -0
- package/dist/components/core/Accordion/AccordionContent.js +25 -0
- package/dist/components/core/Accordion/AccordionItem.js +56 -0
- package/dist/components/core/Accordion/AccordionTrigger.js +32 -0
- package/dist/components/core/Accordion/index.js +5 -0
- package/dist/components/core/Avatar/Avatar.js +94 -0
- package/dist/components/core/Avatar/Avatar.theme.js +25 -0
- package/dist/components/core/AvatarGroup/AvatarGroup.animations.js +79 -0
- package/dist/components/core/AvatarGroup/AvatarGroup.js +67 -0
- package/dist/components/core/AvatarGroup/AvatarGroup.theme.js +23 -0
- package/dist/components/core/Badge/Badge.animations.js +109 -0
- package/dist/components/core/Badge/Badge.js +101 -0
- package/dist/components/core/Badge/Badge.theme.js +41 -0
- package/dist/components/core/Breadcrumbs/Breadcrumbs.theme.js +8 -8
- package/dist/components/core/Button/Button.theme.js +5 -5
- package/dist/components/core/Card/Card.js +46 -0
- package/dist/components/core/Card/Card.theme.js +5 -0
- package/dist/components/core/Divider/Divider.js +21 -0
- package/dist/components/core/Divider/Divider.theme.js +19 -0
- package/dist/components/core/Pagination/Pagination.js +113 -0
- package/dist/components/core/Pagination/Pagination.theme.js +27 -0
- package/dist/components/core/Segmented/Segmented.js +69 -0
- package/dist/components/core/Segmented/Segmented.theme.js +40 -0
- package/dist/components/core/Segmented/SegmentedContext.js +8 -0
- package/dist/components/core/Segmented/SegmentedItem.js +30 -0
- package/dist/components/core/Stepper/Stepper.js +57 -0
- package/dist/components/core/Stepper/Stepper.theme.js +9 -0
- package/dist/components/core/Stepper/components/ConnectorLine.js +42 -0
- package/dist/components/core/Stepper/components/IconCircle.js +44 -0
- package/dist/components/core/Stepper/components/ProgressIndicator.js +12 -0
- package/dist/components/core/Stepper/components/StepContent.js +36 -0
- package/dist/components/core/Stepper/components/StepperItem.js +22 -0
- package/dist/components/core/Tabs/Tabs.theme.js +2 -2
- package/dist/components/core/Tooltip/Tooltip.animations.js +46 -0
- package/dist/components/core/Tooltip/Tooltip.js +85 -0
- package/dist/components/core/Tooltip/Tooltip.theme.js +11 -0
- package/dist/components/core/Tooltip/useTooltipPositioning.js +59 -0
- package/dist/components/core/Tree/Tree.animations.js +38 -0
- package/dist/components/core/Tree/Tree.js +177 -0
- package/dist/components/core/Tree/Tree.theme.js +11 -0
- package/dist/components/data-display/Table/Table.js +177 -0
- package/dist/components/data-display/Table/Table.theme.js +28 -0
- package/dist/components/data-display/Table/Table.utils.js +28 -0
- package/dist/components/data-display/Table/components/DeclarativeComponents.js +123 -0
- package/dist/components/data-display/Table/components/TableActions/TableActions.js +56 -0
- package/dist/components/data-display/Table/components/TableActions/TableActionsMenu.js +29 -0
- package/dist/components/data-display/Table/components/TableColumnManager/TableColumnManager.js +85 -0
- package/dist/components/data-display/Table/components/TableColumnManager/TableColumnManager.theme.js +21 -0
- package/dist/components/data-display/Table/components/TablePagination/TablePagination.js +51 -0
- package/dist/components/data-display/Table/components/TableSelectionHeader/TableSelectionHeader.js +29 -0
- package/dist/components/data-display/Table/components/core/TableBody.js +32 -0
- package/dist/components/data-display/Table/components/core/TableCell.js +47 -0
- package/dist/components/data-display/Table/components/core/TableHeader.js +77 -0
- package/dist/components/data-display/Table/components/core/TableRow.js +46 -0
- package/dist/components/data-display/Table/index.js +20 -0
- package/dist/components/feedback/Alert/Alert.js +36 -0
- package/dist/components/feedback/Alert/Alert.theme.js +17 -0
- package/dist/components/feedback/ChatMessage/ChatMessage.js +26 -0
- package/dist/components/feedback/ChatMessage/ChatMessage.theme.js +16 -0
- package/dist/components/feedback/Empty/Empty.js +26 -0
- package/dist/components/feedback/Empty/Empty.theme.js +13 -0
- package/dist/components/feedback/Notification/Notification.js +41 -0
- package/dist/components/feedback/Notification/Notification.theme.js +49 -0
- package/dist/components/feedback/Spinner/Spinner.theme.js +3 -3
- package/dist/components/feedback/Toast/Toast.animations.js +58 -0
- package/dist/components/feedback/Toast/Toast.js +67 -0
- package/dist/components/feedback/Toast/Toast.theme.js +18 -0
- package/dist/components/feedback/Toast/ToastProvider.js +73 -0
- package/dist/components/feedback/Toast/useToast.js +91 -0
- package/dist/components/forms/Checkbox/Checkbox.theme.js +1 -1
- package/dist/components/forms/FormLabel/FormLabel.theme.js +2 -2
- package/dist/components/forms/Input/Input.theme.js +4 -4
- package/dist/components/forms/Radio/Radio.theme.js +2 -2
- package/dist/components/forms/Select/Select.js +1 -1
- package/dist/components/forms/Select/Select.theme.js +5 -5
- package/dist/components/forms/Switch/Switch.theme.js +1 -1
- package/dist/components/forms/TimePicker/TimePicker.theme.js +19 -19
- package/dist/components/forms/forms.theme.js +8 -8
- package/dist/components/navigation/NavItem/NavItem.js +93 -0
- package/dist/components/navigation/NavItem/NavItem.theme.js +27 -0
- package/dist/components/navigation/Sidebar/Sidebar.js +28 -0
- package/dist/components/navigation/Sidebar/Sidebar.theme.js +16 -0
- package/dist/components/navigation/Sidebar/SidebarContent.js +12 -0
- package/dist/components/navigation/Sidebar/SidebarContext.js +38 -0
- package/dist/components/navigation/Sidebar/SidebarFooter.js +11 -0
- package/dist/components/navigation/Sidebar/SidebarHeader.js +22 -0
- package/dist/components/navigation/Sidebar/SidebarNav.js +11 -0
- package/dist/components/navigation/Sidebar/SidebarNavGroup.js +19 -0
- package/dist/components/navigation/Sidebar/SidebarToggle.js +26 -0
- package/dist/icons/core/TickIcon.js +3 -1
- package/dist/index.d.ts +29 -4
- package/dist/index.js +64 -4
- package/dist/node_modules/@heroicons/react/20/solid/esm/ChevronDownIcon.js +26 -0
- package/dist/node_modules/@heroicons/react/24/outline/esm/ChevronDoubleLeftIcon.js +28 -0
- package/dist/node_modules/@heroicons/react/24/outline/esm/ChevronDoubleRightIcon.js +28 -0
- package/dist/node_modules/@heroicons/react/24/outline/esm/ChevronLeftIcon.js +28 -0
- package/dist/node_modules/@heroicons/react/24/outline/esm/Cog6ToothIcon.js +32 -0
- package/dist/node_modules/@heroicons/react/24/outline/esm/DocumentIcon.js +28 -0
- package/dist/node_modules/@heroicons/react/24/outline/esm/EllipsisVerticalIcon.js +28 -0
- package/dist/node_modules/@heroicons/react/24/outline/esm/PlusIcon.js +28 -0
- package/dist/node_modules/@heroicons/react/24/solid/esm/ArrowPathIcon.js +26 -0
- package/dist/node_modules/@heroicons/react/24/solid/esm/BellIcon.js +26 -0
- package/dist/node_modules/@heroicons/react/24/solid/esm/CheckCircleIcon.js +26 -0
- package/dist/node_modules/@heroicons/react/24/solid/esm/ExclamationTriangleIcon.js +26 -0
- package/dist/node_modules/@heroicons/react/24/solid/esm/InformationCircleIcon.js +26 -0
- package/dist/node_modules/@heroicons/react/24/solid/esm/XCircleIcon.js +26 -0
- package/dist/node_modules/@heroicons/react/24/solid/esm/XMarkIcon.js +26 -0
- package/dist/node_modules/tslib/tslib.es6.js +15 -1
- package/dist/utils/dateUtils.js +32 -0
- package/dist/utils/debounce.js +21 -0
- package/package.json +3 -4
- package/dist/styles.css +0 -2
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import { __rest, __assign } from '../../../node_modules/tslib/tslib.es6.js';
|
|
2
|
+
import React__default, { useRef, useState, useEffect } from 'react';
|
|
3
|
+
import { cn } from '../../../utils/cn.js';
|
|
4
|
+
import { useChartTheme } from '../hooks/useChartTheme.js';
|
|
5
|
+
import { useTooltipPosition } from '../hooks/useTooltipPosition.js';
|
|
6
|
+
import { useChartAccessibility } from '../hooks/useChartAccessibility.js';
|
|
7
|
+
import { useChartValidation } from '../hooks/useChartValidation.js';
|
|
8
|
+
import { ChartErrorBoundary } from '../shared/ChartErrorBoundary/ChartErrorBoundary.js';
|
|
9
|
+
import { XAxis } from '../shared/ChartAxis/XAxis.js';
|
|
10
|
+
import { YAxis } from '../shared/ChartAxis/YAxis.js';
|
|
11
|
+
import { HorizontalGrid } from '../shared/ChartGrid/HorizontalGrid.js';
|
|
12
|
+
import { ChartTooltip } from '../shared/ChartTooltip/ChartTooltip.js';
|
|
13
|
+
import { getColorValue } from '../utils/color-utils.js';
|
|
14
|
+
import { BAR_CHART_DEFAULTS } from './BarChart.types.js';
|
|
15
|
+
|
|
16
|
+
var BarChart = function (_a) {
|
|
17
|
+
var _b;
|
|
18
|
+
var data = _a.data, config = _a.config,
|
|
19
|
+
// Standardized display props with defaults
|
|
20
|
+
_c = _a.showGrid,
|
|
21
|
+
// Standardized display props with defaults
|
|
22
|
+
showGrid = _c === void 0 ? BAR_CHART_DEFAULTS.showGrid : _c, _d = _a.showXAxis, showXAxis = _d === void 0 ? BAR_CHART_DEFAULTS.showXAxis : _d, _e = _a.showYAxis, showYAxis = _e === void 0 ? BAR_CHART_DEFAULTS.showYAxis : _e, _f = _a.showNullValues, showNullValues = _f === void 0 ? BAR_CHART_DEFAULTS.showNullValues : _f, propMinValue = _a.minValue, propMaxValue = _a.maxValue, _g = _a.variant, variant = _g === void 0 ? BAR_CHART_DEFAULTS.variant : _g, theme = _a.theme,
|
|
23
|
+
// Standardized visual props with defaults (renamed from bar-specific names)
|
|
24
|
+
_h = _a.radius,
|
|
25
|
+
// Standardized visual props with defaults (renamed from bar-specific names)
|
|
26
|
+
radius = _h === void 0 ? BAR_CHART_DEFAULTS.radius : _h, // Was barRadius
|
|
27
|
+
_j = _a.gap, // Was barRadius
|
|
28
|
+
gap = _j === void 0 ? BAR_CHART_DEFAULTS.gap : _j, // Was barGap
|
|
29
|
+
_k = _a.categoryGap, // Was barGap
|
|
30
|
+
categoryGap = _k === void 0 ? BAR_CHART_DEFAULTS.categoryGap : _k, // Was barCategoryGap
|
|
31
|
+
_l = _a.stacked, // Was barCategoryGap
|
|
32
|
+
stacked = _l === void 0 ? BAR_CHART_DEFAULTS.stacked : _l,
|
|
33
|
+
// Bar-specific props
|
|
34
|
+
_m = _a.orientation;
|
|
35
|
+
// Bar-specific props
|
|
36
|
+
_m === void 0 ? BAR_CHART_DEFAULTS.orientation : _m;
|
|
37
|
+
var // Base props
|
|
38
|
+
className = _a.className, title = _a.title, description = _a.description, _o = _a.enableKeyboardNavigation, enableKeyboardNavigation = _o === void 0 ? BAR_CHART_DEFAULTS.enableKeyboardNavigation : _o, children = _a.children, props = __rest(_a, ["data", "config", "showGrid", "showXAxis", "showYAxis", "showNullValues", "minValue", "maxValue", "variant", "theme", "radius", "gap", "categoryGap", "stacked", "orientation", "className", "title", "description", "enableKeyboardNavigation", "children"]);
|
|
39
|
+
// Validate data and config with error handling
|
|
40
|
+
var _p = useChartValidation(data, config, {
|
|
41
|
+
autoSanitize: true,
|
|
42
|
+
logWarnings: true,
|
|
43
|
+
}), validation = _p.validation, sanitizedData = _p.sanitizedData, isValid = _p.isValid, hasWarnings = _p.hasWarnings, safeScale = _p.safeScale, safeMath = _p.safeMath;
|
|
44
|
+
// Early return for invalid data with error message
|
|
45
|
+
if (!isValid) {
|
|
46
|
+
return (React__default.createElement("div", __assign({ className: cn("chart-container flex flex-col items-center justify-center p-6 border border-warning-200 bg-warning-50 rounded-lg text-center", className), role: "alert", "aria-live": "polite" }, props),
|
|
47
|
+
React__default.createElement("div", { className: "flex items-center gap-2 mb-2" },
|
|
48
|
+
React__default.createElement("svg", { className: "w-5 h-5 text-warning-500", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24" },
|
|
49
|
+
React__default.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" })),
|
|
50
|
+
React__default.createElement("h3", { className: "text-sm font-medium text-warning-800" }, "Invalid Chart Data")),
|
|
51
|
+
React__default.createElement("p", { className: "text-sm text-warning-600 mb-2" }, ((_b = validation.errors[0]) === null || _b === void 0 ? void 0 : _b.message) || 'Unable to render chart due to data validation errors'),
|
|
52
|
+
process.env.NODE_ENV === 'development' && validation.errors.length > 1 && (React__default.createElement("details", { className: "text-xs text-warning-500" },
|
|
53
|
+
React__default.createElement("summary", { className: "cursor-pointer" },
|
|
54
|
+
"Show all errors (",
|
|
55
|
+
validation.errors.length,
|
|
56
|
+
")"),
|
|
57
|
+
React__default.createElement("ul", { className: "mt-2 text-left" }, validation.errors.map(function (error, index) { return (React__default.createElement("li", { key: index },
|
|
58
|
+
"\u2022 ",
|
|
59
|
+
error.message)); }))))));
|
|
60
|
+
}
|
|
61
|
+
// Early return for empty data
|
|
62
|
+
if (!sanitizedData || sanitizedData.length === 0) {
|
|
63
|
+
return (React__default.createElement("div", __assign({ className: cn("chart-container flex items-center justify-center text-neutral-500", className), role: "img", "aria-label": "Empty bar chart" }, props),
|
|
64
|
+
React__default.createElement("span", null, "No data available")));
|
|
65
|
+
}
|
|
66
|
+
// Use the new theme system
|
|
67
|
+
var _q = useChartTheme({ config: config, variant: variant, theme: theme }); _q.getSeriesColor; _q.getColorClass; _q.getOpacityClass; _q.getTransitionClass;
|
|
68
|
+
// Legacy support
|
|
69
|
+
_q.getThemeValue;
|
|
70
|
+
var containerRef = useRef(null);
|
|
71
|
+
var _r = useState({ width: 600, height: 400 }), dimensions = _r[0], setDimensions = _r[1];
|
|
72
|
+
var _s = useState(null), hoveredCategory = _s[0], setHoveredCategory = _s[1];
|
|
73
|
+
// Detect if device is mobile/touch
|
|
74
|
+
var isMobile = typeof window !== 'undefined' &&
|
|
75
|
+
('ontouchstart' in window || navigator.maxTouchPoints > 0);
|
|
76
|
+
// Use the new tooltip positioning hook
|
|
77
|
+
var _t = useTooltipPosition({
|
|
78
|
+
containerRef: containerRef,
|
|
79
|
+
isMobile: isMobile,
|
|
80
|
+
}), tooltipData = _t.tooltipData, tooltipRef = _t.tooltipRef, handleMouseEnter = _t.handleMouseEnter, handleMouseMove = _t.handleMouseMove, handleMouseLeave = _t.handleMouseLeave;
|
|
81
|
+
// Use accessibility hook
|
|
82
|
+
var _u = useChartAccessibility({
|
|
83
|
+
chartType: 'bar',
|
|
84
|
+
data: sanitizedData,
|
|
85
|
+
config: config,
|
|
86
|
+
title: title,
|
|
87
|
+
description: description,
|
|
88
|
+
enableKeyboardNavigation: enableKeyboardNavigation,
|
|
89
|
+
}), chartAccessibilityProps = _u.chartAccessibilityProps, descriptionProps = _u.descriptionProps, focusedElementIndex = _u.focusedElementIndex, isKeyboardMode = _u.isKeyboardMode, handleMouseInteraction = _u.handleMouseInteraction;
|
|
90
|
+
// Measure container dimensions
|
|
91
|
+
useEffect(function () {
|
|
92
|
+
var updateDimensions = function () {
|
|
93
|
+
if (containerRef.current) {
|
|
94
|
+
var _a = containerRef.current.getBoundingClientRect(), width = _a.width, height = _a.height;
|
|
95
|
+
setDimensions({
|
|
96
|
+
width: width || 600,
|
|
97
|
+
height: height || 400,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
updateDimensions();
|
|
102
|
+
var resizeObserver = new ResizeObserver(updateDimensions);
|
|
103
|
+
if (containerRef.current) {
|
|
104
|
+
resizeObserver.observe(containerRef.current);
|
|
105
|
+
}
|
|
106
|
+
return function () {
|
|
107
|
+
resizeObserver.disconnect();
|
|
108
|
+
};
|
|
109
|
+
}, []);
|
|
110
|
+
// Helper function to create a path for bars with only top corners rounded
|
|
111
|
+
var createRoundedTopBarPath = function (x, y, width, height, radius) {
|
|
112
|
+
var r = Math.min(radius, width / 2, height / 2);
|
|
113
|
+
return "\n M ".concat(x, ",").concat(y + height, "\n L ").concat(x, ",").concat(y + r, "\n Q ").concat(x, ",").concat(y, " ").concat(x + r, ",").concat(y, "\n L ").concat(x + width - r, ",").concat(y, "\n Q ").concat(x + width, ",").concat(y, " ").concat(x + width, ",").concat(y + r, "\n L ").concat(x + width, ",").concat(y + height, "\n Z\n ").trim();
|
|
114
|
+
};
|
|
115
|
+
// Calculate dimensions with safe math
|
|
116
|
+
var margin = {
|
|
117
|
+
top: 15,
|
|
118
|
+
right: showYAxis ? 20 : 0,
|
|
119
|
+
bottom: showXAxis ? 20 : 0,
|
|
120
|
+
left: showYAxis ? 60 : 0,
|
|
121
|
+
};
|
|
122
|
+
var innerWidth = Math.max(0, dimensions.width - margin.left - margin.right);
|
|
123
|
+
var innerHeight = Math.max(0, dimensions.height - margin.top - margin.bottom);
|
|
124
|
+
// Get all data keys from config
|
|
125
|
+
var dataKeys = Object.keys(config || {});
|
|
126
|
+
// Helper function to calculate average of non-null values for a specific key across all data items
|
|
127
|
+
var calculateAverageForKey = function (key) {
|
|
128
|
+
var nonNullValues = sanitizedData
|
|
129
|
+
.map(function (item) { return item[key]; })
|
|
130
|
+
.filter(function (value) { return typeof value === 'number' && isFinite(value); });
|
|
131
|
+
if (nonNullValues.length === 0)
|
|
132
|
+
return 0;
|
|
133
|
+
var sum = nonNullValues.reduce(function (acc, val) { return acc + val; }, 0);
|
|
134
|
+
return safeMath.divide(sum, nonNullValues.length);
|
|
135
|
+
};
|
|
136
|
+
// Calculate safe scale range
|
|
137
|
+
var scaleRange = safeScale.calculateRange({
|
|
138
|
+
minValue: propMinValue,
|
|
139
|
+
maxValue: propMaxValue,
|
|
140
|
+
includeZero: true,
|
|
141
|
+
});
|
|
142
|
+
var minValue = scaleRange.min, maxValue = scaleRange.max, hasValidData = scaleRange.hasValidData;
|
|
143
|
+
// If no valid data, show empty state
|
|
144
|
+
if (!hasValidData) {
|
|
145
|
+
return (React__default.createElement("div", __assign({ className: cn("chart-container flex items-center justify-center text-neutral-500", className), role: "img", "aria-label": "Empty bar chart - no valid data" }, props),
|
|
146
|
+
React__default.createElement("span", null, "No valid numeric data available")));
|
|
147
|
+
}
|
|
148
|
+
// Safe bar width calculations
|
|
149
|
+
var totalCategoryGap = Math.max(0, (sanitizedData.length - 1) * categoryGap);
|
|
150
|
+
var availableWidth = Math.max(0, innerWidth - totalCategoryGap);
|
|
151
|
+
var barWidth = safeMath.divide(availableWidth, sanitizedData.length, 0);
|
|
152
|
+
var totalBarGap = Math.max(0, (dataKeys.length - 1) * gap);
|
|
153
|
+
var availableBarWidth = Math.max(0, barWidth - totalBarGap);
|
|
154
|
+
var individualBarWidth = stacked
|
|
155
|
+
? barWidth
|
|
156
|
+
: safeMath.divide(availableBarWidth, dataKeys.length, 0);
|
|
157
|
+
// Handle keyboard focus tooltips
|
|
158
|
+
useEffect(function () {
|
|
159
|
+
var _a, _b;
|
|
160
|
+
if (isKeyboardMode && focusedElementIndex >= 0 && focusedElementIndex < sanitizedData.length) {
|
|
161
|
+
var item = sanitizedData[focusedElementIndex];
|
|
162
|
+
if (dataKeys.length > 0) {
|
|
163
|
+
var key = dataKeys[0]; // Show first series for keyboard navigation
|
|
164
|
+
var value = item[key];
|
|
165
|
+
if (typeof value === 'number' && isFinite(value)) {
|
|
166
|
+
// Calculate position for focused bar with safe math
|
|
167
|
+
var x = margin.left + focusedElementIndex * (barWidth + categoryGap);
|
|
168
|
+
var range = maxValue - minValue;
|
|
169
|
+
var barHeight = range > 0 ? ((value - minValue) / range) * innerHeight : 0;
|
|
170
|
+
var y = margin.top + innerHeight - barHeight;
|
|
171
|
+
// Get the color class for the first series (index 0)
|
|
172
|
+
var colorClass = void 0;
|
|
173
|
+
switch (0 % 4) {
|
|
174
|
+
case 0:
|
|
175
|
+
colorClass = "fill-primary-base";
|
|
176
|
+
break;
|
|
177
|
+
case 1:
|
|
178
|
+
colorClass = "fill-warning-base";
|
|
179
|
+
break;
|
|
180
|
+
case 2:
|
|
181
|
+
colorClass = "fill-success-base";
|
|
182
|
+
break;
|
|
183
|
+
case 3:
|
|
184
|
+
colorClass = "fill-danger-base";
|
|
185
|
+
break;
|
|
186
|
+
default:
|
|
187
|
+
colorClass = "fill-primary-base";
|
|
188
|
+
}
|
|
189
|
+
var content = {
|
|
190
|
+
category: item.name || "Category ".concat(focusedElementIndex + 1),
|
|
191
|
+
series: [{
|
|
192
|
+
key: key,
|
|
193
|
+
label: ((_a = config[key]) === null || _a === void 0 ? void 0 : _a.label) || key,
|
|
194
|
+
value: value,
|
|
195
|
+
color: ((_b = config[key]) === null || _b === void 0 ? void 0 : _b.color) || getColorValue(colorClass)
|
|
196
|
+
}]
|
|
197
|
+
};
|
|
198
|
+
var svgCoordinates = {
|
|
199
|
+
x: x + barWidth / 2,
|
|
200
|
+
y: y,
|
|
201
|
+
dimensions: dimensions
|
|
202
|
+
};
|
|
203
|
+
// Simulate mouse enter for keyboard focus
|
|
204
|
+
var syntheticEvent = new MouseEvent('mouseenter', {
|
|
205
|
+
clientX: x + barWidth / 2,
|
|
206
|
+
clientY: y,
|
|
207
|
+
});
|
|
208
|
+
handleMouseEnter(syntheticEvent, content, svgCoordinates);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
else if (isKeyboardMode && focusedElementIndex === -1) {
|
|
213
|
+
// Only clear tooltip when explicitly exiting keyboard mode (Escape key)
|
|
214
|
+
handleMouseLeave();
|
|
215
|
+
}
|
|
216
|
+
// Don't clear tooltips when switching from keyboard to mouse mode
|
|
217
|
+
}, [isKeyboardMode, focusedElementIndex, sanitizedData, config, dimensions, margin, innerWidth, innerHeight, categoryGap, minValue, maxValue, handleMouseEnter, handleMouseLeave, dataKeys, barWidth]);
|
|
218
|
+
return (React__default.createElement(ChartErrorBoundary, { className: className, showErrorDetails: process.env.NODE_ENV === 'development' },
|
|
219
|
+
React__default.createElement("div", __assign({ ref: containerRef, className: cn("chart-container w-full h-full", className) }, chartAccessibilityProps, { onMouseEnter: handleMouseInteraction, onMouseMove: handleMouseInteraction }, props),
|
|
220
|
+
descriptionProps && React__default.createElement("div", __assign({}, descriptionProps)),
|
|
221
|
+
process.env.NODE_ENV === 'development' && hasWarnings && (React__default.createElement("div", { className: "absolute top-2 left-2 right-2 z-10 p-2 bg-yellow-50 border border-yellow-200 rounded text-xs text-yellow-800 shadow-sm" },
|
|
222
|
+
React__default.createElement("strong", null, "Chart Warnings:"),
|
|
223
|
+
React__default.createElement("ul", { className: "mt-1" }, validation.warnings.map(function (warning, index) { return (React__default.createElement("li", { key: index },
|
|
224
|
+
"\u2022 ",
|
|
225
|
+
warning.message)); })))),
|
|
226
|
+
React__default.createElement("svg", { width: "100%", height: "100%", viewBox: "0 0 ".concat(dimensions.width, " ").concat(dimensions.height) },
|
|
227
|
+
React__default.createElement(HorizontalGrid, { show: showGrid, x: margin.left, y: margin.top, width: innerWidth, height: innerHeight, lineCount: 5 }),
|
|
228
|
+
React__default.createElement(YAxis, { show: showYAxis, min: minValue, max: maxValue, x: margin.left, y: margin.top, height: innerHeight, showGrid: false, gridWidth: innerWidth, tickFormatter: function (value) {
|
|
229
|
+
if (value >= 1000)
|
|
230
|
+
return "".concat((value / 1000).toFixed(0), "k");
|
|
231
|
+
return value.toString();
|
|
232
|
+
} }),
|
|
233
|
+
React__default.createElement("g", { className: "chart-bars" }, sanitizedData.map(function (item, index) {
|
|
234
|
+
var x = margin.left + index * (barWidth + categoryGap);
|
|
235
|
+
if (stacked) {
|
|
236
|
+
var stackedY_1 = margin.top + innerHeight;
|
|
237
|
+
return (React__default.createElement("g", { key: index }, dataKeys.map(function (key, keyIndex) {
|
|
238
|
+
var rawValue = item[key];
|
|
239
|
+
var value = typeof rawValue === 'number' && isFinite(rawValue) ? rawValue : 0;
|
|
240
|
+
var isNull = rawValue === null || rawValue === undefined || (typeof rawValue === 'number' && !isFinite(rawValue));
|
|
241
|
+
// For null values, use the average of non-null values in this data item
|
|
242
|
+
var actualValue = isNull ? calculateAverageForKey(key) : value;
|
|
243
|
+
var range = maxValue - minValue;
|
|
244
|
+
var barHeight = range > 0 ? safeMath.scale(actualValue, minValue, maxValue, 0, innerHeight) : 0;
|
|
245
|
+
var currentY = stackedY_1 - barHeight;
|
|
246
|
+
stackedY_1 = currentY;
|
|
247
|
+
// Get color class for this data series
|
|
248
|
+
var colorClass;
|
|
249
|
+
switch (keyIndex % 4) {
|
|
250
|
+
case 0:
|
|
251
|
+
colorClass = "fill-primary-base";
|
|
252
|
+
break;
|
|
253
|
+
case 1:
|
|
254
|
+
colorClass = "fill-warning-base";
|
|
255
|
+
break;
|
|
256
|
+
case 2:
|
|
257
|
+
colorClass = "fill-success-base";
|
|
258
|
+
break;
|
|
259
|
+
case 3:
|
|
260
|
+
colorClass = "fill-danger-base";
|
|
261
|
+
break;
|
|
262
|
+
default:
|
|
263
|
+
colorClass = "fill-primary-base";
|
|
264
|
+
}
|
|
265
|
+
var isHovered = hoveredCategory === index;
|
|
266
|
+
var isAnyBarHovered = hoveredCategory !== null;
|
|
267
|
+
var shouldReduceOpacity = isAnyBarHovered && !isHovered;
|
|
268
|
+
var isFocused = isKeyboardMode && focusedElementIndex === index;
|
|
269
|
+
// Don't render null bars if showNullValues is false
|
|
270
|
+
if (isNull && !showNullValues) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
// Skip rendering if bar dimensions are invalid
|
|
274
|
+
if (barHeight <= 0 || individualBarWidth <= 0) {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
return (React__default.createElement("path", { key: keyIndex, d: createRoundedTopBarPath(x, currentY, individualBarWidth, barHeight, keyIndex === dataKeys.length - 1 ? radius : 0), className: cn("transition-all duration-750",
|
|
278
|
+
// Only add cursor-pointer for non-null bars
|
|
279
|
+
!isNull && "cursor-pointer", !isNull && shouldReduceOpacity ? "fill-slate-500" :
|
|
280
|
+
isNull ? "fill-neutral-300 " : colorClass, !isNull && shouldReduceOpacity && "opacity-20",
|
|
281
|
+
// Keyboard focus styles
|
|
282
|
+
isFocused && "stroke-primary-600 stroke-2"), onMouseEnter: !isNull ? function (e) {
|
|
283
|
+
var _a, _b;
|
|
284
|
+
setHoveredCategory(index);
|
|
285
|
+
// Create content in MultiSeriesData format for consistency
|
|
286
|
+
var content = {
|
|
287
|
+
category: item.name || "Category ".concat(index + 1),
|
|
288
|
+
series: [{
|
|
289
|
+
key: key,
|
|
290
|
+
label: ((_a = config[key]) === null || _a === void 0 ? void 0 : _a.label) || key,
|
|
291
|
+
value: value,
|
|
292
|
+
color: ((_b = config[key]) === null || _b === void 0 ? void 0 : _b.color) || getColorValue(colorClass)
|
|
293
|
+
}]
|
|
294
|
+
};
|
|
295
|
+
var svgCoordinates = {
|
|
296
|
+
x: x + individualBarWidth / 2, // Center of bar
|
|
297
|
+
y: currentY, // Top of bar
|
|
298
|
+
dimensions: dimensions
|
|
299
|
+
};
|
|
300
|
+
handleMouseEnter(e, content, svgCoordinates);
|
|
301
|
+
} : undefined, onMouseMove: !isNull ? function (e) {
|
|
302
|
+
if (hoveredCategory === index) {
|
|
303
|
+
var svgCoordinates = {
|
|
304
|
+
x: x + individualBarWidth / 2,
|
|
305
|
+
y: currentY,
|
|
306
|
+
dimensions: dimensions
|
|
307
|
+
};
|
|
308
|
+
handleMouseMove(e, svgCoordinates);
|
|
309
|
+
}
|
|
310
|
+
} : undefined, onMouseLeave: !isNull ? function () {
|
|
311
|
+
setHoveredCategory(null);
|
|
312
|
+
handleMouseLeave();
|
|
313
|
+
} : undefined }));
|
|
314
|
+
})));
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
return (React__default.createElement("g", { key: index }, dataKeys.map(function (key, keyIndex) {
|
|
318
|
+
var rawValue = item[key];
|
|
319
|
+
var value = typeof rawValue === 'number' && isFinite(rawValue) ? rawValue : 0;
|
|
320
|
+
var isNull = rawValue === null || rawValue === undefined || (typeof rawValue === 'number' && !isFinite(rawValue));
|
|
321
|
+
// For null values, use the average of non-null values in this data item
|
|
322
|
+
var actualValue = isNull ? calculateAverageForKey(key) : value;
|
|
323
|
+
var range = maxValue - minValue;
|
|
324
|
+
var barHeight = range > 0 ? safeMath.scale(actualValue, minValue, maxValue, 0, innerHeight) : 0;
|
|
325
|
+
var barX = x + keyIndex * (individualBarWidth + gap);
|
|
326
|
+
var barY = margin.top + innerHeight - barHeight;
|
|
327
|
+
// Get color class for this data series
|
|
328
|
+
var colorClass;
|
|
329
|
+
switch (keyIndex % 4) {
|
|
330
|
+
case 0:
|
|
331
|
+
colorClass = "fill-primary-base";
|
|
332
|
+
break;
|
|
333
|
+
case 1:
|
|
334
|
+
colorClass = "fill-warning-base";
|
|
335
|
+
break;
|
|
336
|
+
case 2:
|
|
337
|
+
colorClass = "fill-success-base";
|
|
338
|
+
break;
|
|
339
|
+
case 3:
|
|
340
|
+
colorClass = "fill-danger-base";
|
|
341
|
+
break;
|
|
342
|
+
default:
|
|
343
|
+
colorClass = "fill-primary-base";
|
|
344
|
+
}
|
|
345
|
+
var isHovered = hoveredCategory === index;
|
|
346
|
+
var isAnyBarHovered = hoveredCategory !== null;
|
|
347
|
+
var shouldReduceOpacity = isAnyBarHovered && !isHovered;
|
|
348
|
+
var isFocused = isKeyboardMode && focusedElementIndex === index;
|
|
349
|
+
// Don't render null bars if showNullValues is false
|
|
350
|
+
if (isNull && !showNullValues) {
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
// Skip rendering if bar dimensions are invalid
|
|
354
|
+
if (barHeight <= 0 || individualBarWidth <= 0) {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
return (React__default.createElement("path", { key: keyIndex, d: createRoundedTopBarPath(barX, barY, individualBarWidth, barHeight, radius), className: cn("transition-all duration-750",
|
|
358
|
+
// Only add cursor-pointer for non-null bars
|
|
359
|
+
!isNull && "cursor-pointer", !isNull && shouldReduceOpacity ? "fill-slate-500" :
|
|
360
|
+
isNull ? "fill-neutral-300" : colorClass, !isNull && shouldReduceOpacity && "opacity-20",
|
|
361
|
+
// Keyboard focus styles
|
|
362
|
+
isFocused && "stroke-primary-600 stroke-2"), onMouseEnter: !isNull ? function (e) {
|
|
363
|
+
var _a, _b;
|
|
364
|
+
setHoveredCategory(index);
|
|
365
|
+
// Create content in MultiSeriesData format for consistency
|
|
366
|
+
var content = {
|
|
367
|
+
category: item.name || "Category ".concat(index + 1),
|
|
368
|
+
series: [{
|
|
369
|
+
key: key,
|
|
370
|
+
label: ((_a = config[key]) === null || _a === void 0 ? void 0 : _a.label) || key,
|
|
371
|
+
value: value,
|
|
372
|
+
color: ((_b = config[key]) === null || _b === void 0 ? void 0 : _b.color) || getColorValue(colorClass)
|
|
373
|
+
}]
|
|
374
|
+
};
|
|
375
|
+
var svgCoordinates = {
|
|
376
|
+
x: barX + individualBarWidth / 2, // Center of bar
|
|
377
|
+
y: barY, // Top of bar
|
|
378
|
+
dimensions: dimensions
|
|
379
|
+
};
|
|
380
|
+
handleMouseEnter(e, content, svgCoordinates);
|
|
381
|
+
} : undefined, onMouseMove: !isNull ? function (e) {
|
|
382
|
+
if (hoveredCategory === index) {
|
|
383
|
+
var svgCoordinates = {
|
|
384
|
+
x: barX + individualBarWidth / 2,
|
|
385
|
+
y: barY,
|
|
386
|
+
dimensions: dimensions
|
|
387
|
+
};
|
|
388
|
+
handleMouseMove(e, svgCoordinates);
|
|
389
|
+
}
|
|
390
|
+
} : undefined, onMouseLeave: !isNull ? function () {
|
|
391
|
+
setHoveredCategory(null);
|
|
392
|
+
handleMouseLeave();
|
|
393
|
+
} : undefined }));
|
|
394
|
+
})));
|
|
395
|
+
}
|
|
396
|
+
})),
|
|
397
|
+
React__default.createElement(XAxis, { show: showXAxis, data: sanitizedData, x: margin.left, y: margin.top + innerHeight, width: innerWidth, categoryWidth: barWidth, categoryGap: categoryGap })),
|
|
398
|
+
React__default.createElement(ChartTooltip, { tooltipRef: tooltipRef, content: tooltipData === null || tooltipData === void 0 ? void 0 : tooltipData.content, active: !!tooltipData, position: tooltipData ? { x: tooltipData.x, y: tooltipData.y } : { x: 0, y: 0 } }),
|
|
399
|
+
children)));
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
export { BarChart };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { __assign } from '../../../node_modules/tslib/tslib.es6.js';
|
|
2
|
+
import { CHART_DEFAULTS } from '../types/chart.types.js';
|
|
3
|
+
|
|
4
|
+
// Default values using centralized defaults
|
|
5
|
+
var BAR_CHART_DEFAULTS = __assign(__assign({}, CHART_DEFAULTS), { radius: 12, gap: CHART_DEFAULTS.gap, categoryGap: CHART_DEFAULTS.categoryGap, orientation: CHART_DEFAULTS.orientation, stacked: CHART_DEFAULTS.stacked });
|
|
6
|
+
|
|
7
|
+
export { BAR_CHART_DEFAULTS };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { __rest, __assign } from '../../node_modules/tslib/tslib.es6.js';
|
|
2
|
+
import React__default, { createContext, useContext } from 'react';
|
|
3
|
+
import { cn } from '../../utils/cn.js';
|
|
4
|
+
import { useChartTheme } from './hooks/useChartTheme.js';
|
|
5
|
+
|
|
6
|
+
var ChartContext = createContext(null);
|
|
7
|
+
function useChart() {
|
|
8
|
+
var context = useContext(ChartContext);
|
|
9
|
+
if (!context) {
|
|
10
|
+
throw new Error('useChart must be used within a ChartContainer');
|
|
11
|
+
}
|
|
12
|
+
return context;
|
|
13
|
+
}
|
|
14
|
+
function ChartContainer(_a) {
|
|
15
|
+
var data = _a.data, config = _a.config, className = _a.className, children = _a.children, _b = _a.minHeight, minHeight = _b === void 0 ? 200 : _b, _c = _a.width, width = _c === void 0 ? '100%' : _c, height = _a.height, props = __rest(_a, ["data", "config", "className", "children", "minHeight", "width", "height"]);
|
|
16
|
+
// Provide default empty config if none provided
|
|
17
|
+
var safeConfig = config || {};
|
|
18
|
+
var chartColors = useChartTheme({
|
|
19
|
+
config: safeConfig,
|
|
20
|
+
variant: 'default'
|
|
21
|
+
}).chartColors;
|
|
22
|
+
// Only apply inline styles if no sizing classes are detected in className
|
|
23
|
+
var hasWidthClass = (className === null || className === void 0 ? void 0 : className.includes('w-')) || false;
|
|
24
|
+
var hasHeightClass = (className === null || className === void 0 ? void 0 : className.includes('h-')) || false;
|
|
25
|
+
var inlineStyles = __assign(__assign(__assign(__assign({}, chartColors), ((!hasWidthClass && width) && {
|
|
26
|
+
width: typeof width === 'number' ? "".concat(width, "px") : width
|
|
27
|
+
})), ((!hasHeightClass && height) && {
|
|
28
|
+
height: typeof height === 'number' ? "".concat(height, "px") : height
|
|
29
|
+
})), ((!hasHeightClass && !height) && {
|
|
30
|
+
minHeight: typeof minHeight === 'number' ? "".concat(minHeight, "px") : minHeight
|
|
31
|
+
}));
|
|
32
|
+
return (React__default.createElement(ChartContext.Provider, { value: { data: data || [], config: safeConfig } },
|
|
33
|
+
React__default.createElement("div", __assign({ className: cn("flex aspect-auto justify-center text-xs",
|
|
34
|
+
// Add w-full as default if no width class is specified
|
|
35
|
+
!hasWidthClass && "w-full", className), style: inlineStyles }, props), children)));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { ChartContainer, useChart };
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { __rest, __assign } from '../../../node_modules/tslib/tslib.es6.js';
|
|
2
|
+
import React__default, { useRef, useState, useMemo, useEffect } from 'react';
|
|
3
|
+
import { cn } from '../../../utils/cn.js';
|
|
4
|
+
import { useChartTheme } from '../hooks/useChartTheme.js';
|
|
5
|
+
import { useTooltipPosition } from '../hooks/useTooltipPosition.js';
|
|
6
|
+
import { useChart } from '../ChartContainer.js';
|
|
7
|
+
import { ChartTooltip } from '../shared/ChartTooltip/ChartTooltip.js';
|
|
8
|
+
import { HeatmapCell } from './HeatmapCell.js';
|
|
9
|
+
import { processHeatmapData, createGridMatrix, calculateIntensity, getDefaultTooltipContent } from './utils/heatmapUtils.js';
|
|
10
|
+
import { getColorValue } from '../utils/color-utils.js';
|
|
11
|
+
import { HeatmapLegend } from './HeatmapLegend.js';
|
|
12
|
+
import { motion } from '../../../node_modules/framer-motion/dist/es/render/components/motion/proxy.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Heatmap component for visualizing data in a grid format with color intensity
|
|
16
|
+
* Supports weekly, monthly, hourly views and custom data structures
|
|
17
|
+
*/
|
|
18
|
+
var Heatmap = function (_a) {
|
|
19
|
+
var dataProp = _a.data, configProp = _a.config, _b = _a.colorScale, colorScale = _b === void 0 ? { minOpacity: 0.1 } : _b, _c = _a.layout, layout = _c === void 0 ? { gap: 6} : _c, _d = _a.axis, axis = _d === void 0 ? { showXAxis: true, showYAxis: true } : _d, _e = _a.tooltip, tooltip = _e === void 0 ? { enabled: true } : _e, _f = _a.legend, legend = _f === void 0 ? { enabled: true } : _f, valueRange = _a.valueRange, _g = _a.showNullValues, showNullValues = _g === void 0 ? true : _g, onCellClick = _a.onCellClick, cellRenderer = _a.cellRenderer, className = _a.className, _h = _a.variant, variant = _h === void 0 ? 'default' : _h, children = _a.children, props = __rest(_a, ["data", "config", "colorScale", "layout", "axis", "tooltip", "legend", "valueRange", "showNullValues", "onCellClick", "cellRenderer", "className", "variant", "children"]);
|
|
20
|
+
// Get data and config from context if not provided as props
|
|
21
|
+
var chartContext = useChart();
|
|
22
|
+
var data = dataProp || chartContext.data;
|
|
23
|
+
var config = configProp || chartContext.config;
|
|
24
|
+
var _j = useChartTheme({
|
|
25
|
+
config: config,
|
|
26
|
+
variant: variant === 'bordered' ? 'default' : variant
|
|
27
|
+
}); _j.getThemeValue; var getSeriesColor = _j.getSeriesColor;
|
|
28
|
+
var containerRef = useRef(null);
|
|
29
|
+
var _k = useState({ width: 600, height: 400 }); _k[0]; var setDimensions = _k[1];
|
|
30
|
+
// Detect if device is mobile/touch
|
|
31
|
+
var isMobile = typeof window !== 'undefined' &&
|
|
32
|
+
('ontouchstart' in window || navigator.maxTouchPoints > 0);
|
|
33
|
+
// Use the new tooltip positioning hook
|
|
34
|
+
var _l = useTooltipPosition({
|
|
35
|
+
containerRef: containerRef,
|
|
36
|
+
isMobile: isMobile,
|
|
37
|
+
}), tooltipData = _l.tooltipData, tooltipRef = _l.tooltipRef, handleMouseEnter = _l.handleMouseEnter, handleMouseMove = _l.handleMouseMove, handleMouseLeave = _l.handleMouseLeave;
|
|
38
|
+
// Process data
|
|
39
|
+
var processedData = useMemo(function () {
|
|
40
|
+
return processHeatmapData(data, valueRange);
|
|
41
|
+
}, [data, valueRange]);
|
|
42
|
+
var xCategories = processedData.xCategories, yCategories = processedData.yCategories, minValue = processedData.minValue, maxValue = processedData.maxValue;
|
|
43
|
+
// Create grid matrix
|
|
44
|
+
var gridMatrix = useMemo(function () {
|
|
45
|
+
return createGridMatrix(data, xCategories, yCategories);
|
|
46
|
+
}, [data, xCategories, yCategories]);
|
|
47
|
+
// Measure container dimensions
|
|
48
|
+
useEffect(function () {
|
|
49
|
+
var updateDimensions = function () {
|
|
50
|
+
if (containerRef.current) {
|
|
51
|
+
var _a = containerRef.current.getBoundingClientRect(), width = _a.width, height = _a.height;
|
|
52
|
+
setDimensions({ width: width, height: height });
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
updateDimensions();
|
|
56
|
+
window.addEventListener('resize', updateDimensions);
|
|
57
|
+
return function () { return window.removeEventListener('resize', updateDimensions); };
|
|
58
|
+
}, []);
|
|
59
|
+
// Get the color for heatmap cells
|
|
60
|
+
var cellColorClass = getSeriesColor('value', 0); // Get the first series color for 'value' key
|
|
61
|
+
var cellColor = getColorValue(cellColorClass); // Convert Tailwind class to CSS color value
|
|
62
|
+
// Handle cell hover using new tooltip system
|
|
63
|
+
var handleCellHover = function (data, event) {
|
|
64
|
+
if (!tooltip.enabled)
|
|
65
|
+
return;
|
|
66
|
+
var content = tooltip.formatter
|
|
67
|
+
? tooltip.formatter(data)
|
|
68
|
+
: getDefaultTooltipContent(data);
|
|
69
|
+
handleMouseEnter(event, content);
|
|
70
|
+
};
|
|
71
|
+
// Handle cell mouse move using new tooltip system
|
|
72
|
+
var handleCellMouseMove = function (data, event) {
|
|
73
|
+
if (!tooltip.enabled || !tooltipData)
|
|
74
|
+
return;
|
|
75
|
+
handleMouseMove(event);
|
|
76
|
+
};
|
|
77
|
+
// Handle cell leave using new tooltip system
|
|
78
|
+
var handleCellLeave = function () {
|
|
79
|
+
if (!tooltip.enabled)
|
|
80
|
+
return;
|
|
81
|
+
handleMouseLeave();
|
|
82
|
+
};
|
|
83
|
+
// Calculate grid styles
|
|
84
|
+
var gridStyles = {
|
|
85
|
+
display: 'grid',
|
|
86
|
+
gridTemplateColumns: "repeat(".concat(xCategories.length, ", 1fr)"),
|
|
87
|
+
gridTemplateRows: "repeat(".concat(yCategories.length, ", 1fr)"),
|
|
88
|
+
gap: typeof layout.gap === 'number' ? "".concat(layout.gap, "px") : layout.gap,
|
|
89
|
+
width: '100%',
|
|
90
|
+
height: '100%',
|
|
91
|
+
};
|
|
92
|
+
// Render axis labels
|
|
93
|
+
var renderXAxisLabels = function () {
|
|
94
|
+
if (!axis.showXAxis)
|
|
95
|
+
return null;
|
|
96
|
+
return (React__default.createElement("div", { className: "flex justify-between items-center px-1 py-2 text-xs text-neutral-600 dark:text-neutral-400 select-none", style: { gridColumn: "1 / -1" }, role: "list", "aria-label": "X-axis labels", "aria-describedby": "heatmap-x-axis-description" },
|
|
97
|
+
xCategories.map(function (category, index) { return (React__default.createElement("div", { key: category, className: "flex-1 text-center font-medium px-1", style: {
|
|
98
|
+
transform: axis.xAxisRotation ? "rotate(".concat(axis.xAxisRotation, "deg)") : undefined
|
|
99
|
+
}, role: "listitem", "aria-label": "X-axis label: ".concat(axis.xAxisFormatter
|
|
100
|
+
? axis.xAxisFormatter(category, index)
|
|
101
|
+
: String(category)) }, axis.xAxisFormatter
|
|
102
|
+
? axis.xAxisFormatter(category, index)
|
|
103
|
+
: String(category))); }),
|
|
104
|
+
React__default.createElement("span", { id: "heatmap-x-axis-description", className: "sr-only" },
|
|
105
|
+
"X-axis contains ",
|
|
106
|
+
xCategories.length,
|
|
107
|
+
" categories representing the horizontal dimension of the heatmap")));
|
|
108
|
+
};
|
|
109
|
+
var renderYAxisLabels = function () {
|
|
110
|
+
if (!axis.showYAxis)
|
|
111
|
+
return null;
|
|
112
|
+
return (React__default.createElement("div", { className: "grid text-xs text-neutral-600 dark:text-neutral-400 pr-3", style: {
|
|
113
|
+
gridTemplateRows: "repeat(".concat(yCategories.length, ", 1fr)"),
|
|
114
|
+
gap: typeof layout.gap === 'number' ? "".concat(layout.gap, "px") : layout.gap,
|
|
115
|
+
height: '100%',
|
|
116
|
+
}, role: "list", "aria-label": "Y-axis labels", "aria-describedby": "heatmap-y-axis-description" },
|
|
117
|
+
yCategories.map(function (category, index) { return (React__default.createElement("div", { key: category, className: "flex items-center justify-end font-medium", style: {
|
|
118
|
+
transform: axis.yAxisRotation ? "rotate(".concat(axis.yAxisRotation, "deg)") : undefined
|
|
119
|
+
}, role: "listitem", "aria-label": "Y-axis label: ".concat(axis.yAxisFormatter
|
|
120
|
+
? axis.yAxisFormatter(category, index)
|
|
121
|
+
: String(category)) }, axis.yAxisFormatter
|
|
122
|
+
? axis.yAxisFormatter(category, index)
|
|
123
|
+
: String(category))); }),
|
|
124
|
+
React__default.createElement("span", { id: "heatmap-y-axis-description", className: "sr-only" },
|
|
125
|
+
"Y-axis contains ",
|
|
126
|
+
yCategories.length,
|
|
127
|
+
" categories representing the vertical dimension of the heatmap")));
|
|
128
|
+
};
|
|
129
|
+
return (React__default.createElement("div", __assign({ ref: containerRef, className: cn('relative w-full h-full min-h-[200px] flex flex-col', variant === 'bordered' && 'border border-neutral-200 dark:border-neutral-700 rounded-lg p-4', variant === 'minimal' && 'p-2', className) }, props),
|
|
130
|
+
React__default.createElement("div", { className: "flex flex-1 min-h-0" },
|
|
131
|
+
axis.showYAxis && (React__default.createElement("div", { className: "flex-shrink-0 w-16 flex flex-col" },
|
|
132
|
+
axis.showXAxis && React__default.createElement("div", { className: "flex-shrink-0 h-8" }),
|
|
133
|
+
React__default.createElement("div", { className: "flex-1" }, renderYAxisLabels()))),
|
|
134
|
+
React__default.createElement("div", { className: "flex-1 flex flex-col min-w-0" },
|
|
135
|
+
axis.showXAxis && (React__default.createElement("div", { className: "flex-shrink-0 h-8" }, renderXAxisLabels())),
|
|
136
|
+
React__default.createElement("div", { className: "flex-1 relative min-h-0" },
|
|
137
|
+
React__default.createElement(motion.div, { style: gridStyles, initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.5, ease: 'easeOut' }, className: "absolute inset-0" }, gridMatrix.flat().map(function (cellData, index) {
|
|
138
|
+
var intensity = calculateIntensity(cellData.value, minValue, maxValue);
|
|
139
|
+
"".concat(cellData.x, "-").concat(cellData.y);
|
|
140
|
+
// Skip null values if not showing them
|
|
141
|
+
if (!showNullValues && cellData.value === null) {
|
|
142
|
+
return (React__default.createElement("div", { key: "".concat(cellData.x, "-").concat(cellData.y, "-").concat(index), className: "bg-transparent" }));
|
|
143
|
+
}
|
|
144
|
+
var CellComponent = cellRenderer || HeatmapCell;
|
|
145
|
+
return (React__default.createElement(CellComponent, { key: "".concat(cellData.x, "-").concat(cellData.y, "-").concat(index), data: cellData, intensity: intensity, minOpacity: colorScale === null || colorScale === void 0 ? void 0 : colorScale.minOpacity, color: cellColor, onClick: onCellClick, onHover: handleCellHover, onMouseMove: handleCellMouseMove, onLeave: handleCellLeave, className: "rounded-sm" }));
|
|
146
|
+
}))))),
|
|
147
|
+
((legend === null || legend === void 0 ? void 0 : legend.enabled) !== false) && (React__default.createElement("div", { className: "flex-shrink-0 mt-2" },
|
|
148
|
+
React__default.createElement(HeatmapLegend, { colorKey: "value", color: cellColor, minValue: minValue, maxValue: maxValue, labels: legend === null || legend === void 0 ? void 0 : legend.labels }))),
|
|
149
|
+
tooltip.enabled && (React__default.createElement(ChartTooltip, { content: tooltipData === null || tooltipData === void 0 ? void 0 : tooltipData.content, active: !!tooltipData, position: tooltipData ? { x: tooltipData.x, y: tooltipData.y } : { x: 0, y: 0 }, tooltipRef: tooltipRef })),
|
|
150
|
+
children));
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export { Heatmap };
|