@czi-sds/data-viz 0.14.0 → 0.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.d.ts +30 -13
- package/dist/index.cjs.js +244 -136
- package/dist/index.esm.d.ts +30 -13
- package/dist/index.esm.js +244 -136
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
package/dist/index.cjs.js
CHANGED
|
@@ -1047,7 +1047,7 @@ const SDSLightThemeColors = {
|
|
|
1047
1047
|
purple: {
|
|
1048
1048
|
"100": "#efeafe",
|
|
1049
1049
|
"200": "#e4dbfc",
|
|
1050
|
-
"300": "#
|
|
1050
|
+
"300": "#cbbaf8",
|
|
1051
1051
|
"400": "#b296f2",
|
|
1052
1052
|
"500": "#8b54e2",
|
|
1053
1053
|
"600": "#6526b5",
|
|
@@ -1218,7 +1218,7 @@ const percentToHex = (percentOpacity) => {
|
|
|
1218
1218
|
// Convert percentage to 0-255 range
|
|
1219
1219
|
const alphaValue = Math.round((clampedPercent / 100) * 255);
|
|
1220
1220
|
// Convert to hex and pad with leading zero if needed
|
|
1221
|
-
return alphaValue.toString(16).padStart(2, "0").
|
|
1221
|
+
return alphaValue.toString(16).padStart(2, "0").toLowerCase();
|
|
1222
1222
|
};
|
|
1223
1223
|
/**
|
|
1224
1224
|
* Adds opacity to a hex color by appending the hex opacity value.
|
|
@@ -1897,10 +1897,10 @@ const fontBodyS$1 = fontBody("s", "regular");
|
|
|
1897
1897
|
const fontBodyXs$2 = fontBody("xs", "regular");
|
|
1898
1898
|
const fontBodyXxs$1 = fontBody("xxs", "regular");
|
|
1899
1899
|
const fontBodyXxxs$1 = fontBody("xxxs", "regular");
|
|
1900
|
-
const fontBodyXxxxs = fontBody("xxxxs", "regular");
|
|
1901
1900
|
const fontBodyMediumL = fontBody("l", "medium");
|
|
1902
1901
|
const fontBodyMediumS = fontBody("s", "medium");
|
|
1903
1902
|
const fontBodyMediumXs = fontBody("xs", "medium");
|
|
1903
|
+
const fontBodyMediumXxxs = fontBody("xxxs", "medium");
|
|
1904
1904
|
const fontBodySemiboldL = fontBody("l", "semibold");
|
|
1905
1905
|
const fontBodySemiboldM = fontBody("m", "semibold");
|
|
1906
1906
|
const fontBodySemiboldS = fontBody("s", "semibold");
|
|
@@ -10539,7 +10539,7 @@ const LegendItem = styled$2__default.default("div") `
|
|
|
10539
10539
|
align-items: center;
|
|
10540
10540
|
cursor: ${disabled ? "not-allowed" : "pointer"} !important;
|
|
10541
10541
|
transition: background-color 0.2s ease-in-out;
|
|
10542
|
-
padding:
|
|
10542
|
+
padding: ${spaces?.xxxs}px ${spaces?.xxs}px;
|
|
10543
10543
|
border-radius: ${corners?.s}px;
|
|
10544
10544
|
background-color: ${isSelected ? semanticColors?.base?.backgroundTertiary : "transparent"};
|
|
10545
10545
|
pointer-events: ${disabled ? "none" : "auto"};
|
|
@@ -10556,8 +10556,8 @@ const LegendIcon = styled$2__default.default("div") `
|
|
|
10556
10556
|
const spaces = getSpaces(props);
|
|
10557
10557
|
const corners = getCorners(props);
|
|
10558
10558
|
return `
|
|
10559
|
-
width:
|
|
10560
|
-
height:
|
|
10559
|
+
width: ${spaces?.m}px;
|
|
10560
|
+
height: ${spaces?.m}px;
|
|
10561
10561
|
border-radius: ${corners?.s}px;
|
|
10562
10562
|
background-color: ${color};
|
|
10563
10563
|
flex-shrink: 0;
|
|
@@ -10568,7 +10568,7 @@ const LegendIcon = styled$2__default.default("div") `
|
|
|
10568
10568
|
}}
|
|
10569
10569
|
`;
|
|
10570
10570
|
const LegendLabel = styled$2__default.default("span") `
|
|
10571
|
-
${
|
|
10571
|
+
${fontBodyMediumXxxs}
|
|
10572
10572
|
${(props) => {
|
|
10573
10573
|
const semanticColors = getSemanticColors(props);
|
|
10574
10574
|
return `
|
|
@@ -10577,13 +10577,13 @@ const LegendLabel = styled$2__default.default("span") `
|
|
|
10577
10577
|
}}
|
|
10578
10578
|
`;
|
|
10579
10579
|
const LegendValue = styled$2__default.default("span") `
|
|
10580
|
-
${
|
|
10580
|
+
${fontBodyMediumXxxs}
|
|
10581
10581
|
${(props) => {
|
|
10582
10582
|
const semanticColors = getSemanticColors(props);
|
|
10583
10583
|
const spaces = getSpaces(props);
|
|
10584
10584
|
return `
|
|
10585
10585
|
color: ${semanticColors?.base?.textSecondary};
|
|
10586
|
-
margin-left: ${spaces?.
|
|
10586
|
+
margin-left: ${spaces?.xxs}px;
|
|
10587
10587
|
`;
|
|
10588
10588
|
}}
|
|
10589
10589
|
`;
|
|
@@ -24775,7 +24775,7 @@ Object.assign(chroma, {
|
|
|
24775
24775
|
const generateDiscreteColors = (count, options = {}) => {
|
|
24776
24776
|
if (count === 0)
|
|
24777
24777
|
return [];
|
|
24778
|
-
const { isDarkMode = false, start =
|
|
24778
|
+
const { isDarkMode = false, start = 250, rotations = 0.85, gamma = 0.7, lightness = [0.4, 0.7], correctLightness = true, } = options;
|
|
24779
24779
|
let scale = chroma
|
|
24780
24780
|
.cubehelix()
|
|
24781
24781
|
.start(start)
|
|
@@ -24811,33 +24811,22 @@ var StyledBadge = styled$2__default.default("div")(templateObject_4 || (template
|
|
|
24811
24811
|
return "\n padding: ".concat(spaces === null || spaces === void 0 ? void 0 : spaces.xxxs, "px ").concat(spaces === null || spaces === void 0 ? void 0 : spaces.xxs, "px;\n border-radius: ").concat(corners === null || corners === void 0 ? void 0 : corners.m, "px;\n background-color: ").concat((_a = semanticColors === null || semanticColors === void 0 ? void 0 : semanticColors.base) === null || _a === void 0 ? void 0 : _a.backgroundTertiary, ";\n color: ").concat((_b = semanticColors === null || semanticColors === void 0 ? void 0 : semanticColors.base) === null || _b === void 0 ? void 0 : _b.textSecondary, ";\n ");
|
|
24812
24812
|
});
|
|
24813
24813
|
var BarContainer = styled$2__default.default("div")(templateObject_5 || (templateObject_5 = __makeTemplateObject(["\n ", "\n"], ["\n ", "\n"])), function (props) {
|
|
24814
|
-
var
|
|
24814
|
+
var _a;
|
|
24815
|
+
var width = props.width, barHeight = props.barHeight, isEmpty = props.isEmpty;
|
|
24815
24816
|
var corners = getCorners(props);
|
|
24817
|
+
var semanticColors = getSemanticColors(props);
|
|
24816
24818
|
var finalWidth = typeof width === "number" ? "".concat(width, "px") : width;
|
|
24817
|
-
return "\n display: flex;\n border-radius: ".concat(corners === null || corners === void 0 ? void 0 : corners.s, "px;\n width: ").concat(finalWidth, ";\n height: ").concat(barHeight, "px;\n ");
|
|
24819
|
+
return "\n display: flex;\n border-radius: ".concat(corners === null || corners === void 0 ? void 0 : corners.s, "px;\n width: ").concat(finalWidth, ";\n height: ").concat(barHeight, "px;\n background-color: ").concat(isEmpty ? (_a = semanticColors === null || semanticColors === void 0 ? void 0 : semanticColors.base) === null || _a === void 0 ? void 0 : _a.backgroundTertiary : "transparent", ";\n overflow: hidden;\n ");
|
|
24818
24820
|
});
|
|
24819
24821
|
var BarSegment = styled$2__default.default("div")(templateObject_6 || (templateObject_6 = __makeTemplateObject(["\n ", "\n"], ["\n ", "\n"])), function (props) {
|
|
24820
|
-
var
|
|
24822
|
+
var color = props.color, height = props.height, percentage = props.percentage, opacity = props.opacity, disabled = props.disabled, isLast = props.isLast;
|
|
24821
24823
|
var spaces = getSpaces(props);
|
|
24822
24824
|
var gap = (spaces === null || spaces === void 0 ? void 0 : spaces.xxxs) || 0;
|
|
24823
|
-
|
|
24824
|
-
var corners = getCorners(props);
|
|
24825
|
-
var borderRadius = corners === null || corners === void 0 ? void 0 : corners.s;
|
|
24826
|
-
if (isFirst && isLast) {
|
|
24827
|
-
return "border-radius: ".concat(borderRadius, "px;");
|
|
24828
|
-
}
|
|
24829
|
-
if (isFirst) {
|
|
24830
|
-
return "\n border-top-left-radius: ".concat(borderRadius, "px; \n border-bottom-left-radius: ").concat(borderRadius, "px;\n ");
|
|
24831
|
-
}
|
|
24832
|
-
if (isLast) {
|
|
24833
|
-
return "\n border-top-right-radius: ".concat(borderRadius, "px; \n border-bottom-right-radius: ").concat(borderRadius, "px;\n ");
|
|
24834
|
-
}
|
|
24835
|
-
return "";
|
|
24836
|
-
};
|
|
24837
|
-
// Add gap as margin-right, except for last item
|
|
24825
|
+
// Add gap as margin-right for spacing between segments
|
|
24838
24826
|
// When percentage is 0, margin also becomes 0 (animates away with the segment)
|
|
24839
|
-
|
|
24840
|
-
|
|
24827
|
+
// Last visible segment also gets no margin
|
|
24828
|
+
var marginRight = percentage > 0 && !isLast ? "".concat(gap, "px") : 0;
|
|
24829
|
+
return "\n background-color: ".concat(color, ";\n height: ").concat(height, "px;\n flex-grow: ").concat(percentage, ";\n flex-basis: 0;\n flex-shrink: 1;\n margin-right: ").concat(marginRight, ";\n opacity: ").concat(opacity, ";\n transition: opacity 0.2s ease-in-out, flex-grow 0.25s ease-in-out, margin-right 0.25s ease-out;\n cursor: ").concat(disabled ? "default" : "pointer", ";\n pointer-events: ").concat(disabled ? "none" : "auto", ";\n ");
|
|
24841
24830
|
});
|
|
24842
24831
|
var StyledStackedBarChartWrapper = styled$2__default.default("div")(templateObject_7 || (templateObject_7 = __makeTemplateObject(["\n ", "\n"], ["\n ", "\n"])), function (props) {
|
|
24843
24832
|
var spaces = getSpaces(props);
|
|
@@ -24847,56 +24836,126 @@ var templateObject_1, templateObject_2, templateObject_3, templateObject_4, temp
|
|
|
24847
24836
|
|
|
24848
24837
|
// Helper: Calculate badge display text based on selection state
|
|
24849
24838
|
var calculateBadgeText = function (totalItems, selectedCount) {
|
|
24850
|
-
if (selectedCount === 0)
|
|
24839
|
+
if (selectedCount === 0 || selectedCount === totalItems)
|
|
24851
24840
|
return "".concat(totalItems);
|
|
24852
|
-
if (selectedCount === totalItems)
|
|
24853
|
-
return "All";
|
|
24854
24841
|
return "".concat(selectedCount, " of ").concat(totalItems);
|
|
24855
24842
|
};
|
|
24856
24843
|
// Helper: Calculate legend value display
|
|
24857
|
-
var calculateLegendValue = function (item,
|
|
24844
|
+
var calculateLegendValue = function (item, showLegendValues, legendValueFormat, globalUnit) {
|
|
24858
24845
|
if (!showLegendValues)
|
|
24859
24846
|
return undefined;
|
|
24860
|
-
if (
|
|
24847
|
+
if (legendValueFormat === "percentage") {
|
|
24861
24848
|
return "".concat(Math.round(item.percentage), "%");
|
|
24862
24849
|
}
|
|
24863
|
-
//
|
|
24850
|
+
// Count format: show value with optional unit
|
|
24864
24851
|
var effectiveUnit = item.unit || globalUnit;
|
|
24865
24852
|
return effectiveUnit ? "".concat(item.value, " ").concat(effectiveUnit) : "".concat(item.value);
|
|
24866
24853
|
};
|
|
24867
24854
|
// Helper: Get segment opacity based on hover and selection state
|
|
24868
|
-
var getSegmentOpacity = function (index, hoveredIndex,
|
|
24869
|
-
var hasSelection =
|
|
24855
|
+
var getSegmentOpacity = function (index, hoveredIndex, selectedIndicesSet, selectionBehavior) {
|
|
24856
|
+
var hasSelection = selectedIndicesSet.size > 0;
|
|
24857
|
+
// Hide mode doesn't use opacity - segments animate to 0% width instead
|
|
24858
|
+
if (selectionBehavior === "hide") {
|
|
24859
|
+
return 1;
|
|
24860
|
+
}
|
|
24861
|
+
// Dim mode
|
|
24870
24862
|
if (hoveredIndex !== null) {
|
|
24871
|
-
return hoveredIndex === index ||
|
|
24863
|
+
return hoveredIndex === index || selectedIndicesSet.has(index) ? 1 : 0.2;
|
|
24872
24864
|
}
|
|
24873
24865
|
if (hasSelection) {
|
|
24874
|
-
return
|
|
24866
|
+
return selectedIndicesSet.has(index) ? 1 : 0.2;
|
|
24875
24867
|
}
|
|
24876
24868
|
return 1;
|
|
24877
24869
|
};
|
|
24870
|
+
// Helper: Apply cumulative mode hide styling
|
|
24871
|
+
var applyCumulativeHideStyling = function (items, selectedIndicesSet, selectedTotal, hasRemaining, effectiveMaxAmount) {
|
|
24872
|
+
var newRemainingValue = hasRemaining
|
|
24873
|
+
? effectiveMaxAmount - selectedTotal
|
|
24874
|
+
: 0;
|
|
24875
|
+
var newRemainingPercentage = hasRemaining
|
|
24876
|
+
? (newRemainingValue / effectiveMaxAmount) * 100
|
|
24877
|
+
: 0;
|
|
24878
|
+
var styledItems = items.map(function (item) {
|
|
24879
|
+
var isSelected = selectedIndicesSet.has(item.originalIndex);
|
|
24880
|
+
return __assign$1(__assign$1({}, item), { percentage: isSelected ? item.percentage : 0 });
|
|
24881
|
+
});
|
|
24882
|
+
return {
|
|
24883
|
+
styledItems: styledItems,
|
|
24884
|
+
updatedRemainingPercentage: newRemainingPercentage,
|
|
24885
|
+
updatedRemainingValue: newRemainingValue,
|
|
24886
|
+
};
|
|
24887
|
+
};
|
|
24888
|
+
// Helper: Apply proportional mode hide styling
|
|
24889
|
+
var applyProportionalHideStyling = function (items, selectedIndicesSet, selectedTotal) {
|
|
24890
|
+
if (selectedTotal === 0) {
|
|
24891
|
+
return {
|
|
24892
|
+
styledItems: items.map(function (item) { return (__assign$1(__assign$1({}, item), { percentage: 0 })); }),
|
|
24893
|
+
updatedRemainingPercentage: 0,
|
|
24894
|
+
updatedRemainingValue: 0,
|
|
24895
|
+
};
|
|
24896
|
+
}
|
|
24897
|
+
var styledItems = items.map(function (item) {
|
|
24898
|
+
var isSelected = selectedIndicesSet.has(item.originalIndex);
|
|
24899
|
+
var newPercentage = isSelected ? (item.value / selectedTotal) * 100 : 0;
|
|
24900
|
+
return __assign$1(__assign$1({}, item), { percentage: newPercentage });
|
|
24901
|
+
});
|
|
24902
|
+
return {
|
|
24903
|
+
styledItems: styledItems,
|
|
24904
|
+
updatedRemainingPercentage: 0,
|
|
24905
|
+
updatedRemainingValue: 0,
|
|
24906
|
+
};
|
|
24907
|
+
};
|
|
24908
|
+
// Helper: Apply selection styling and recalculate percentages for hide mode
|
|
24909
|
+
var applySelectionStyling = function (items, selectedIndicesSet, selectionBehavior, mode, hasRemaining, effectiveMaxAmount, baseRemainingPercentage, baseRemainingValue) {
|
|
24910
|
+
var hasSelection = selectedIndicesSet.size > 0;
|
|
24911
|
+
// If no selection or dim mode, return items as-is with base remaining values
|
|
24912
|
+
if (selectionBehavior !== "hide" || !hasSelection) {
|
|
24913
|
+
return {
|
|
24914
|
+
styledItems: items,
|
|
24915
|
+
updatedRemainingPercentage: baseRemainingPercentage,
|
|
24916
|
+
updatedRemainingValue: baseRemainingValue,
|
|
24917
|
+
};
|
|
24918
|
+
}
|
|
24919
|
+
// Calculate total of selected items
|
|
24920
|
+
var selectedTotal = items
|
|
24921
|
+
.filter(function (item) { return selectedIndicesSet.has(item.originalIndex); })
|
|
24922
|
+
.reduce(function (sum, item) { return sum + item.value; }, 0);
|
|
24923
|
+
// Apply mode-specific styling
|
|
24924
|
+
return mode === "cumulative"
|
|
24925
|
+
? applyCumulativeHideStyling(items, selectedIndicesSet, selectedTotal, hasRemaining, effectiveMaxAmount)
|
|
24926
|
+
: applyProportionalHideStyling(items, selectedIndicesSet, selectedTotal);
|
|
24927
|
+
};
|
|
24878
24928
|
// Helper: Build legend items from data with optional remaining segment
|
|
24879
24929
|
var buildLegendItems = function (dataWithPercentages, options) {
|
|
24880
|
-
var _a, _b, _c
|
|
24881
|
-
var
|
|
24930
|
+
var _a, _b, _c;
|
|
24931
|
+
var showLegendValues = options.showLegendValues, legendValueFormat = options.legendValueFormat, unit = options.unit, hasRemaining = options.hasRemaining, showLegend = options.showLegend, remainingValue = options.remainingValue, remainingPercentage = options.remainingPercentage, remainingLabel = options.remainingLabel, remainingUnit = options.remainingUnit, theme = options.theme;
|
|
24882
24932
|
var legendItems = dataWithPercentages.map(function (item) { return ({
|
|
24883
24933
|
name: item.name,
|
|
24884
|
-
value: calculateLegendValue(item,
|
|
24934
|
+
value: calculateLegendValue(item, showLegendValues, legendValueFormat, unit),
|
|
24885
24935
|
color: item.color,
|
|
24886
24936
|
disabled: item.disabled,
|
|
24887
24937
|
}); });
|
|
24888
24938
|
// Add remaining item to legend if applicable
|
|
24889
24939
|
if (hasRemaining && showLegend) {
|
|
24890
|
-
var
|
|
24891
|
-
|
|
24892
|
-
|
|
24940
|
+
var remainingValueDisplay = void 0;
|
|
24941
|
+
if (!showLegendValues) {
|
|
24942
|
+
remainingValueDisplay = undefined;
|
|
24943
|
+
}
|
|
24944
|
+
else if (legendValueFormat === "percentage") {
|
|
24945
|
+
remainingValueDisplay = "".concat(Math.round(remainingPercentage), "%");
|
|
24946
|
+
}
|
|
24947
|
+
else {
|
|
24948
|
+
// Count format
|
|
24949
|
+
// Use remainingUnit or global unit (never a custom unit from data items)
|
|
24950
|
+
var effectiveRemainingUnit = remainingUnit || unit || "";
|
|
24951
|
+
remainingValueDisplay = effectiveRemainingUnit
|
|
24893
24952
|
? "".concat(remainingValue, " ").concat(effectiveRemainingUnit)
|
|
24894
|
-
: "".concat(remainingValue)
|
|
24895
|
-
|
|
24953
|
+
: "".concat(remainingValue);
|
|
24954
|
+
}
|
|
24896
24955
|
legendItems.push({
|
|
24897
24956
|
name: remainingLabel,
|
|
24898
24957
|
value: remainingValueDisplay,
|
|
24899
|
-
color: (
|
|
24958
|
+
color: (_c = (_b = (_a = theme === null || theme === void 0 ? void 0 : theme.palette) === null || _a === void 0 ? void 0 : _a.sds) === null || _b === void 0 ? void 0 : _b.base) === null || _c === void 0 ? void 0 : _c.backgroundTertiary,
|
|
24900
24959
|
disabled: true,
|
|
24901
24960
|
});
|
|
24902
24961
|
}
|
|
@@ -24904,8 +24963,10 @@ var buildLegendItems = function (dataWithPercentages, options) {
|
|
|
24904
24963
|
};
|
|
24905
24964
|
// Helper: Render a bar segment with optional tooltip
|
|
24906
24965
|
var renderBarSegment = function (item, index, options) {
|
|
24907
|
-
var
|
|
24908
|
-
|
|
24966
|
+
var barHeight = options.barHeight, hoveredIndex = options.hoveredIndex, selectedIndicesSet = options.selectedIndicesSet, selectionBehavior = options.selectionBehavior, isLast = options.isLast, onMouseEnter = options.onMouseEnter, onMouseLeave = options.onMouseLeave, onClick = options.onClick;
|
|
24967
|
+
// Mark hidden segments with data-hide attribute when in hide mode
|
|
24968
|
+
var isHidden = selectionBehavior === "hide" && item.percentage === 0;
|
|
24969
|
+
var barSegment = (jsxRuntime.jsx(BarSegment, { color: item.color, percentage: item.percentage, height: barHeight, opacity: getSegmentOpacity(item.originalIndex, hoveredIndex, selectedIndicesSet, selectionBehavior), isLast: isLast, "data-hide": isHidden, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onClick: onClick }));
|
|
24909
24970
|
return item.tooltip !== undefined ? (jsxRuntime.jsx(Tooltip, { title: null, componentSlot: jsxRuntime.jsx(TooltipTableContent, __assign$1({}, item.tooltip)), placement: "top", hasInvertedStyle: false, open: hoveredIndex === item.originalIndex, disableInteractive: true, PopperProps: {
|
|
24910
24971
|
disablePortal: false,
|
|
24911
24972
|
modifiers: [
|
|
@@ -24920,18 +24981,20 @@ var renderBarSegment = function (item, index, options) {
|
|
|
24920
24981
|
};
|
|
24921
24982
|
var StackedBarChart = function (props) {
|
|
24922
24983
|
var _a, _b, _c;
|
|
24923
|
-
var title = props.title, badge = props.badge, _d = props.hideBadge, hideBadge = _d === void 0 ? false : _d, data = props.data, _e = props.width, width = _e === void 0 ? "100%" : _e, _f = props.barHeight,
|
|
24984
|
+
var title = props.title, badge = props.badge, _d = props.hideBadge, hideBadge = _d === void 0 ? false : _d, data = props.data, _e = props.width, width = _e === void 0 ? "100%" : _e, _f = props.barHeight, barHeightProp = _f === void 0 ? 16 : _f, _g = props.showLegend, showLegend = _g === void 0 ? true : _g, _h = props.showLegendValues, showLegendValues = _h === void 0 ? true : _h, _j = props.legendValueFormat, legendValueFormat = _j === void 0 ? "percentage" : _j, _k = props.selectedIndices, selectedIndices = _k === void 0 ? [] : _k, onSelectionChange = props.onSelectionChange, _l = props.selectionBehavior, selectionBehavior = _l === void 0 ? "dim" : _l, _m = props.mode, mode = _m === void 0 ? "proportional" : _m, maxAmount = props.maxAmount, _o = props.remainingLabel, remainingLabel = _o === void 0 ? "Remaining" : _o, remainingUnit = props.remainingUnit, unit = props.unit, colorGeneratorOptions = props.colorGeneratorOptions, rest = __rest(props, ["title", "badge", "hideBadge", "data", "width", "barHeight", "showLegend", "showLegendValues", "legendValueFormat", "selectedIndices", "onSelectionChange", "selectionBehavior", "mode", "maxAmount", "remainingLabel", "remainingUnit", "unit", "colorGeneratorOptions"]);
|
|
24985
|
+
// Ensure barHeight is at least 1px
|
|
24986
|
+
var barHeight = Math.max(1, barHeightProp);
|
|
24924
24987
|
var theme = material.useTheme();
|
|
24925
|
-
var
|
|
24926
|
-
var
|
|
24927
|
-
var
|
|
24928
|
-
var
|
|
24988
|
+
var _p = React.useState(null), hoveredIndex = _p[0], setHoveredIndex = _p[1];
|
|
24989
|
+
var _q = React.useState([]), animatedItems = _q[0], setAnimatedItems = _q[1];
|
|
24990
|
+
var _r = React.useState(new Map()), autoColorMap = _r[0], setAutoColorMap = _r[1];
|
|
24991
|
+
var _s = React.useState(0), animatedRemainingPercentage = _s[0], setAnimatedRemainingPercentage = _s[1];
|
|
24929
24992
|
var previousProcessedRef = React.useRef([]);
|
|
24930
24993
|
var previousDataKeysRef = React.useRef(new Set());
|
|
24931
24994
|
var previousColorOptionsRef = React.useRef(null);
|
|
24932
24995
|
var previousRemainingPercentageRef = React.useRef(0);
|
|
24933
|
-
//
|
|
24934
|
-
var animateItemRemoval = function (previousItems, nextItems, currentRemainingPercentage, newRemainingPercentage) {
|
|
24996
|
+
// Animate items when removals occur
|
|
24997
|
+
var animateItemRemoval = React.useCallback(function (previousItems, nextItems, currentRemainingPercentage, newRemainingPercentage) {
|
|
24935
24998
|
if (typeof window === "undefined") {
|
|
24936
24999
|
setAnimatedItems(nextItems);
|
|
24937
25000
|
previousProcessedRef.current = nextItems;
|
|
@@ -24990,9 +25053,9 @@ var StackedBarChart = function (props) {
|
|
|
24990
25053
|
frameIds.forEach(function (id) { return window.cancelAnimationFrame(id); });
|
|
24991
25054
|
window.clearTimeout(timeoutId);
|
|
24992
25055
|
};
|
|
24993
|
-
};
|
|
25056
|
+
}, []);
|
|
24994
25057
|
// Detect dark mode from theme
|
|
24995
|
-
var isDarkMode = getMode({ theme: theme }) === "dark";
|
|
25058
|
+
var isDarkMode = React.useMemo(function () { return getMode({ theme: theme }) === "dark"; }, [theme]);
|
|
24996
25059
|
// Helper to generate color map for auto items
|
|
24997
25060
|
var generateColorMap = React.useCallback(function (items) {
|
|
24998
25061
|
var palette = generateDiscreteColors(items.length, __assign$1(__assign$1({}, (colorGeneratorOptions || {})), { isDarkMode: isDarkMode }));
|
|
@@ -25012,8 +25075,7 @@ var StackedBarChart = function (props) {
|
|
|
25012
25075
|
// Check if color generation options changed
|
|
25013
25076
|
var colorOptionsChanged = !previousColorOptions ||
|
|
25014
25077
|
previousColorOptions.isDarkMode !== isDarkMode ||
|
|
25015
|
-
|
|
25016
|
-
JSON.stringify(colorGeneratorOptions);
|
|
25078
|
+
previousColorOptions.options !== colorGeneratorOptions;
|
|
25017
25079
|
// Update refs
|
|
25018
25080
|
previousDataKeysRef.current = currentDataKeys;
|
|
25019
25081
|
previousColorOptionsRef.current = {
|
|
@@ -25046,36 +25108,38 @@ var StackedBarChart = function (props) {
|
|
|
25046
25108
|
setAutoColorMap(updatedMap);
|
|
25047
25109
|
}
|
|
25048
25110
|
}
|
|
25049
|
-
|
|
25050
|
-
|
|
25051
|
-
|
|
25052
|
-
|
|
25053
|
-
|
|
25054
|
-
|
|
25055
|
-
|
|
25056
|
-
|
|
25057
|
-
|
|
25058
|
-
|
|
25059
|
-
|
|
25060
|
-
|
|
25061
|
-
|
|
25062
|
-
|
|
25063
|
-
|
|
25064
|
-
|
|
25065
|
-
|
|
25066
|
-
|
|
25067
|
-
|
|
25068
|
-
|
|
25069
|
-
: 0;
|
|
25111
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
25112
|
+
}, [data, colorGeneratorOptions, isDarkMode, theme, generateColorMap]);
|
|
25113
|
+
// Calculate display badge value
|
|
25114
|
+
var displayBadge = badge !== null && badge !== void 0 ? badge : calculateBadgeText(data.length, selectedIndices.length);
|
|
25115
|
+
// Calculate totals and remaining segments for cumulative mode
|
|
25116
|
+
var _t = React.useMemo(function () {
|
|
25117
|
+
var total = data.reduce(function (sum, item) { return sum + item.value; }, 0);
|
|
25118
|
+
var maxAmountValue = mode === "cumulative" && maxAmount ? maxAmount : total;
|
|
25119
|
+
var hasRemainingSegment = mode === "cumulative" && total < maxAmountValue;
|
|
25120
|
+
var remaining = hasRemainingSegment ? maxAmountValue - total : 0;
|
|
25121
|
+
var remainingPct = hasRemainingSegment
|
|
25122
|
+
? (remaining / maxAmountValue) * 100
|
|
25123
|
+
: 0;
|
|
25124
|
+
return {
|
|
25125
|
+
effectiveMaxAmount: maxAmountValue,
|
|
25126
|
+
hasRemaining: hasRemainingSegment,
|
|
25127
|
+
remainingValue: remaining,
|
|
25128
|
+
remainingPercentage: remainingPct,
|
|
25129
|
+
};
|
|
25130
|
+
}, [data, mode, maxAmount]), effectiveMaxAmount = _t.effectiveMaxAmount, hasRemaining = _t.hasRemaining, remainingValue = _t.remainingValue, remainingPercentage = _t.remainingPercentage;
|
|
25070
25131
|
// Calculate percentages and assign colors for each segment
|
|
25071
25132
|
// default to ornament secondary color if no color is provided
|
|
25072
|
-
|
|
25073
|
-
|
|
25074
|
-
|
|
25075
|
-
|
|
25076
|
-
|
|
25077
|
-
|
|
25078
|
-
|
|
25133
|
+
// Memoize to prevent unnecessary recalculations and re-renders
|
|
25134
|
+
var dataWithPercentages = React.useMemo(function () {
|
|
25135
|
+
return data.map(function (item, index) {
|
|
25136
|
+
var _a, _b, _c;
|
|
25137
|
+
var resolvedColor = item.color ||
|
|
25138
|
+
autoColorMap.get(item.name) ||
|
|
25139
|
+
((_c = (_b = (_a = theme === null || theme === void 0 ? void 0 : theme.palette) === null || _a === void 0 ? void 0 : _a.sds) === null || _b === void 0 ? void 0 : _b.base) === null || _c === void 0 ? void 0 : _c.ornamentSecondary);
|
|
25140
|
+
return __assign$1(__assign$1({}, item), { color: resolvedColor, percentage: effectiveMaxAmount > 0 ? (item.value / effectiveMaxAmount) * 100 : 0, key: item.name, originalIndex: index });
|
|
25141
|
+
});
|
|
25142
|
+
}, [data, effectiveMaxAmount, autoColorMap, theme]);
|
|
25079
25143
|
// Handle data changes and animate exits
|
|
25080
25144
|
React.useEffect(function () {
|
|
25081
25145
|
var previousProcessed = previousProcessedRef.current;
|
|
@@ -25095,53 +25159,104 @@ var StackedBarChart = function (props) {
|
|
|
25095
25159
|
previousProcessedRef.current = dataWithPercentages;
|
|
25096
25160
|
setAnimatedRemainingPercentage(remainingPercentage);
|
|
25097
25161
|
previousRemainingPercentageRef.current = remainingPercentage;
|
|
25098
|
-
|
|
25099
|
-
|
|
25100
|
-
|
|
25101
|
-
|
|
25102
|
-
|
|
25103
|
-
|
|
25104
|
-
|
|
25105
|
-
|
|
25106
|
-
|
|
25107
|
-
|
|
25108
|
-
|
|
25109
|
-
|
|
25110
|
-
|
|
25111
|
-
|
|
25112
|
-
|
|
25113
|
-
|
|
25114
|
-
|
|
25115
|
-
|
|
25116
|
-
|
|
25117
|
-
|
|
25118
|
-
var
|
|
25119
|
-
|
|
25120
|
-
|
|
25121
|
-
|
|
25122
|
-
|
|
25162
|
+
}, [dataWithPercentages, remainingPercentage, animateItemRemoval]);
|
|
25163
|
+
// Create a Set for O(1) lookups of selected indices
|
|
25164
|
+
var selectedIndicesSet = React.useMemo(function () { return new Set(selectedIndices); }, [selectedIndices]);
|
|
25165
|
+
// Apply selection styling for hide mode
|
|
25166
|
+
// This modifies percentages but keeps all items (including hidden ones at 0%)
|
|
25167
|
+
// so animations continue to work smoothly
|
|
25168
|
+
var _u = React.useMemo(function () {
|
|
25169
|
+
return applySelectionStyling(animatedItems, selectedIndicesSet, selectionBehavior, mode, hasRemaining, effectiveMaxAmount, animatedRemainingPercentage, remainingValue);
|
|
25170
|
+
}, [
|
|
25171
|
+
animatedItems,
|
|
25172
|
+
selectedIndicesSet,
|
|
25173
|
+
selectionBehavior,
|
|
25174
|
+
mode,
|
|
25175
|
+
hasRemaining,
|
|
25176
|
+
effectiveMaxAmount,
|
|
25177
|
+
animatedRemainingPercentage,
|
|
25178
|
+
remainingValue,
|
|
25179
|
+
]), styledItems = _u.styledItems, updatedRemainingPercentage = _u.updatedRemainingPercentage, updatedRemainingValue = _u.updatedRemainingValue;
|
|
25180
|
+
// Calculate display values for hide mode
|
|
25181
|
+
var isHideMode = selectionBehavior === "hide" && selectedIndices.length > 0;
|
|
25182
|
+
var displayRemainingPercentage = isHideMode
|
|
25183
|
+
? updatedRemainingPercentage
|
|
25184
|
+
: animatedRemainingPercentage;
|
|
25185
|
+
var displayRemainingValue = isHideMode
|
|
25186
|
+
? updatedRemainingValue
|
|
25187
|
+
: remainingValue;
|
|
25188
|
+
// Convert data to legend items format (always use full data, not filtered)
|
|
25189
|
+
// Use updated remaining values when in hide mode with selections
|
|
25190
|
+
var legendItems = React.useMemo(function () {
|
|
25191
|
+
return buildLegendItems(dataWithPercentages, {
|
|
25192
|
+
showLegendValues: showLegendValues,
|
|
25193
|
+
legendValueFormat: legendValueFormat,
|
|
25194
|
+
unit: unit,
|
|
25195
|
+
hasRemaining: hasRemaining,
|
|
25196
|
+
showLegend: showLegend,
|
|
25197
|
+
remainingValue: displayRemainingValue,
|
|
25198
|
+
remainingPercentage: displayRemainingPercentage,
|
|
25199
|
+
remainingLabel: remainingLabel,
|
|
25200
|
+
remainingUnit: remainingUnit,
|
|
25201
|
+
theme: theme,
|
|
25202
|
+
});
|
|
25203
|
+
}, [
|
|
25204
|
+
dataWithPercentages,
|
|
25205
|
+
showLegendValues,
|
|
25206
|
+
legendValueFormat,
|
|
25207
|
+
unit,
|
|
25208
|
+
hasRemaining,
|
|
25209
|
+
showLegend,
|
|
25210
|
+
displayRemainingValue,
|
|
25211
|
+
displayRemainingPercentage,
|
|
25212
|
+
remainingLabel,
|
|
25213
|
+
remainingUnit,
|
|
25214
|
+
theme,
|
|
25215
|
+
]);
|
|
25216
|
+
// Handle legend item hover and leave
|
|
25217
|
+
var handleLegendItemHover = React.useCallback(function (_item, index) { return setHoveredIndex(index); }, []);
|
|
25218
|
+
var handleLegendItemLeave = React.useCallback(function () { return setHoveredIndex(null); }, []);
|
|
25219
|
+
// Handle selection change - reusable logic for both segment and legend
|
|
25220
|
+
var handleSelectionChange = React.useCallback(function (newSelectedIndices) {
|
|
25123
25221
|
if (!onSelectionChange)
|
|
25124
25222
|
return;
|
|
25125
|
-
var isSelected = selectedIndices.includes(index);
|
|
25126
|
-
var newSelectedIndices;
|
|
25127
|
-
if (isSelected) {
|
|
25128
|
-
newSelectedIndices = selectedIndices.filter(function (i) { return i !== index; });
|
|
25129
|
-
}
|
|
25130
|
-
else {
|
|
25131
|
-
newSelectedIndices = __spreadArray(__spreadArray([], selectedIndices, true), [index], false);
|
|
25132
|
-
}
|
|
25133
25223
|
var selectedData = data.filter(function (item, itemIndex) {
|
|
25134
25224
|
return newSelectedIndices.includes(itemIndex);
|
|
25135
25225
|
});
|
|
25136
25226
|
onSelectionChange(newSelectedIndices, selectedData);
|
|
25137
|
-
};
|
|
25138
|
-
|
|
25227
|
+
}, [onSelectionChange, data]);
|
|
25228
|
+
// Handle segment click - toggle selection
|
|
25229
|
+
var handleSegmentClick = React.useCallback(function (index) {
|
|
25230
|
+
if (!onSelectionChange)
|
|
25231
|
+
return;
|
|
25232
|
+
var isSelected = selectedIndicesSet.has(index);
|
|
25233
|
+
var newSelectedIndices = isSelected
|
|
25234
|
+
? selectedIndices.filter(function (i) { return i !== index; })
|
|
25235
|
+
: __spreadArray(__spreadArray([], selectedIndices, true), [index], false);
|
|
25236
|
+
handleSelectionChange(newSelectedIndices);
|
|
25237
|
+
}, [
|
|
25238
|
+
onSelectionChange,
|
|
25239
|
+
selectedIndices,
|
|
25240
|
+
selectedIndicesSet,
|
|
25241
|
+
handleSelectionChange,
|
|
25242
|
+
]);
|
|
25243
|
+
// Calculate the last visible segment (percentage > 0)
|
|
25244
|
+
var lastVisibleIndex = React.useMemo(function () {
|
|
25245
|
+
for (var i = styledItems.length - 1; i >= 0; i--) {
|
|
25246
|
+
if (styledItems[i].percentage > 0) {
|
|
25247
|
+
return i;
|
|
25248
|
+
}
|
|
25249
|
+
}
|
|
25250
|
+
return -1;
|
|
25251
|
+
}, [styledItems]);
|
|
25252
|
+
var chartContent = (jsxRuntime.jsxs(StyledStackedBarChartWrapper, { children: [jsxRuntime.jsxs(BarContainer, { width: width, barHeight: barHeight, isEmpty: styledItems.length === 0 && !hasRemaining, children: [styledItems.map(function (item, index) {
|
|
25253
|
+
var isLastVisible = index === lastVisibleIndex && !hasRemaining;
|
|
25139
25254
|
return renderBarSegment(item, index, {
|
|
25140
|
-
isFirst: index === 0,
|
|
25141
|
-
isLast: index === animatedItems.length - 1 && !hasRemaining,
|
|
25142
25255
|
barHeight: barHeight,
|
|
25143
25256
|
hoveredIndex: hoveredIndex,
|
|
25144
|
-
|
|
25257
|
+
selectedIndicesSet: selectedIndicesSet,
|
|
25258
|
+
selectionBehavior: selectionBehavior,
|
|
25259
|
+
isLast: isLastVisible,
|
|
25145
25260
|
onMouseEnter: function () {
|
|
25146
25261
|
return !item.isExiting && setHoveredIndex(item.originalIndex);
|
|
25147
25262
|
},
|
|
@@ -25150,14 +25265,7 @@ var StackedBarChart = function (props) {
|
|
|
25150
25265
|
return !item.isExiting && handleSegmentClick(item.originalIndex);
|
|
25151
25266
|
},
|
|
25152
25267
|
});
|
|
25153
|
-
}), hasRemaining && (jsxRuntime.jsx(BarSegment, { color: (_c = (_b = (_a = theme === null || theme === void 0 ? void 0 : theme.palette) === null || _a === void 0 ? void 0 : _a.sds) === null || _b === void 0 ? void 0 : _b.base) === null || _c === void 0 ? void 0 : _c.backgroundTertiary, percentage:
|
|
25154
|
-
? function (newSelectedIndices) {
|
|
25155
|
-
var selectedData = data.filter(function (item, itemIndex) {
|
|
25156
|
-
return newSelectedIndices.includes(itemIndex);
|
|
25157
|
-
});
|
|
25158
|
-
onSelectionChange(newSelectedIndices, selectedData);
|
|
25159
|
-
}
|
|
25160
|
-
: undefined, hoveredIndex: hoveredIndex }))] }));
|
|
25268
|
+
}), hasRemaining && (jsxRuntime.jsx(BarSegment, { color: (_c = (_b = (_a = theme === null || theme === void 0 ? void 0 : theme.palette) === null || _a === void 0 ? void 0 : _a.sds) === null || _b === void 0 ? void 0 : _b.base) === null || _c === void 0 ? void 0 : _c.backgroundTertiary, percentage: displayRemainingPercentage, height: barHeight, isLast: true, opacity: 1, disabled: true }, "remaining"))] }), showLegend && (jsxRuntime.jsx(Legend, { items: legendItems, showValues: showLegendValues, onItemMouseEnter: handleLegendItemHover, onItemMouseLeave: handleLegendItemLeave, selectedIndices: selectedIndices, onSelectionChange: onSelectionChange ? handleSelectionChange : undefined, hoveredIndex: hoveredIndex }))] }));
|
|
25161
25269
|
// If no title, render chart and optional legend without wrapper
|
|
25162
25270
|
if (!title) {
|
|
25163
25271
|
return (jsxRuntime.jsx(ChartWrapper, __assign$1({ width: width }, rest, { children: chartContent })));
|