@razorpay/blade
Version:
The Design System that powers Razorpay
738 lines (705 loc) • 32 kB
JavaScript
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties';
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import React__default from 'react';
import styled from 'styled-components';
import '../../../node_modules/recharts/es6/index.js';
import '../utils/index.js';
import { X_AXIS_TICK_LINE_HEIGHT, X_AXIS_TICK_START_DY, X_AXIS_LABEL_GAP, X_AXIS_LABEL_OFFSET, X_AXIS_LABEL_HEIGHT, X_OFFSET, componentId, LEGEND_MARGIN_TOP, TEXT_BASELINE, PADDING_HORIZONTAL, PADDING_VERTICAL, RECT_HEIGHT } from './tokens.js';
import { calculateTextWidth } from './utils.js';
import { useCommonChartComponentsContext } from './CommonChartComponentsContext.js';
import getIn from '../../../utils/lodashButBetter/get.js';
import '../../Typography/index.js';
import '../../Box/index.js';
import '../../BladeProvider/index.js';
import '../../../utils/assignWithoutSideEffects/index.js';
import { useControllableState } from '../../../utils/useControllable.js';
import { jsx, jsxs } from 'react/jsx-runtime';
import { sanitizeString } from '../utils/sanitizeString/sanitizeString.js';
import { isSequentialColor } from '../utils/isSequentialColor.js';
import { getHighestColorInRange } from '../utils/getHighestColorInRange.js';
import { totalChartColors } from '../utils/tokens.js';
import useTheme from '../../BladeProvider/useTheme.js';
import { XAxis } from '../../../node_modules/recharts/es6/cartesian/XAxis.js';
import { assignWithoutSideEffects } from '../../../utils/assignWithoutSideEffects/assignWithoutSideEffects.js';
import { YAxis } from '../../../node_modules/recharts/es6/cartesian/YAxis.js';
import { CartesianGrid } from '../../../node_modules/recharts/es6/cartesian/CartesianGrid.js';
import { Box } from '../../Box/Box.js';
import { Text } from '../../Typography/Text/Text.js';
import { Tooltip } from '../../../node_modules/recharts/es6/component/Tooltip.js';
import { Heading } from '../../Typography/Heading/Heading.js';
import { Legend } from '../../../node_modules/recharts/es6/component/Legend.js';
import { ReferenceLine } from '../../../node_modules/recharts/es6/cartesian/ReferenceLine.js';
var _excluded = ["interval", "tickLine", "label", "dataKey", "height", "tickFormatter"],
_excluded2 = ["secondaryDataKey"],
_excluded3 = ["selectedDataKeys", "defaultSelectedDataKeys", "onSelectedDataKeysChange"];
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
var getChartColor = function getChartColor(dataKey, name, dataColorMapping, chartName) {
var colorKey = chartName === 'donut' ? sanitizeString(name !== null && name !== void 0 ? name : '') : dataKey;
var mappedColorData = dataColorMapping === null || dataColorMapping === void 0 ? void 0 : dataColorMapping[colorKey !== null && colorKey !== void 0 ? colorKey : ''];
// Handle case where mappedColorData is undefined (e.g., during dynamic nameKey changes)
if (!mappedColorData) {
return 'data.background.categorical.azure.faint';
}
var mappedColor = mappedColorData.colorToken;
var isCustomColor = mappedColorData.isCustomColor;
if ((chartName === 'line' || chartName === 'area') && !isCustomColor) {
return mappedColor;
}
if (mappedColor && isSequentialColor(mappedColor)) {
return mappedColor !== null && mappedColor !== void 0 ? mappedColor : 'data.background.categorical.azure.faint';
}
return getHighestColorInRange({
colorToken: mappedColor !== null && mappedColor !== void 0 ? mappedColor : 'data.background.categorical.azure.faint',
followIntensityMapping: chartName === 'donut' && (isCustomColor || Object.keys(dataColorMapping).length > totalChartColors)
});
};
/**
* Wraps text to fit within a given pixel width, breaking at word boundaries.
* Returns an array of lines that each fit within the max width.
*/
var wrapTextToFit = function wrapTextToFit(text, maxWidthPx, fontSize) {
// Approximate average character width (varies by font, but ~0.5-0.6 of font size is common)
var avgCharWidth = fontSize * 0.55;
var maxCharsPerLine = Math.max(1, Math.floor(maxWidthPx / avgCharWidth));
// If text fits in one line, return as-is
if (text.length <= maxCharsPerLine) {
return [text];
}
var words = text.split(' ');
var lines = [];
var currentLine = '';
var _iterator = _createForOfIteratorHelper(words),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var word = _step.value;
if (currentLine.length === 0) {
currentLine = word;
} else if (currentLine.length + 1 + word.length <= maxCharsPerLine) {
currentLine += " ".concat(word);
} else {
lines.push(currentLine);
currentLine = word;
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
if (currentLine.length > 0) {
lines.push(currentLine);
}
return lines;
};
/**
* Reusable component for rendering wrapped text labels in SVG.
* Handles multi-line text rendering with configurable positioning.
*/
var wrappedTextLabel = function wrappedTextLabel(_ref) {
var text = _ref.text,
maxWidth = _ref.maxWidth,
fontSize = _ref.fontSize,
lineHeight = _ref.lineHeight,
startDy = _ref.startDy,
textAnchor = _ref.textAnchor,
fill = _ref.fill,
fontFamily = _ref.fontFamily,
fontWeight = _ref.fontWeight,
letterSpacing = _ref.letterSpacing,
_ref$yOffset = _ref.yOffset,
yOffset = _ref$yOffset === void 0 ? 0 : _ref$yOffset;
var lines = wrapTextToFit(text, maxWidth, fontSize);
var totalHeight = lines.length * lineHeight;
var element = /*#__PURE__*/jsx("text", {
x: 0,
y: yOffset,
textAnchor: textAnchor,
fill: fill,
fontSize: fontSize,
fontFamily: fontFamily,
fontWeight: fontWeight,
style: {
letterSpacing: letterSpacing
},
children: lines.map(function (line, index) {
return /*#__PURE__*/jsx("tspan", {
x: 0,
dy: index === 0 ? startDy : lineHeight,
children: line
}, index);
})
});
return {
element: element,
height: totalHeight
};
};
/**
* Custom tick component for X-axis with automatic text wrapping.
* - Primary label: Automatically wraps long text to multiple lines based on available width
* - Secondary label: Optional label from pre-computed secondaryLabelMap (shown below primary, also supports wrapping)
* - Edge alignment: For line/area charts, first/last ticks align start/end to prevent clipping.
* For bar charts, all labels are center-aligned since bars are centered on tick positions.
* - Reports calculated height via onHeightCalculated callback for dynamic axis sizing
*/
var CustomXAxisTick = function CustomXAxisTick(_ref2) {
var x = _ref2.x,
y = _ref2.y,
payload = _ref2.payload,
secondaryLabelMap = _ref2.secondaryLabelMap,
theme = _ref2.theme,
tickWidth = _ref2.tickWidth,
chartName = _ref2.chartName,
onHeightCalculated = _ref2.onHeightCalculated,
lastTick = _ref2.lastTick,
tickFormatter = _ref2.tickFormatter;
var fontSize = theme.typography.fonts.size[75];
var maxWidth = tickWidth ? tickWidth * 0.9 : Infinity;
// For bar charts, always center align labels since bars are centered on ticks
// For line/area charts, align first tick left and last tick right to prevent clipping
var shouldUseEdgeAlignment = chartName === 'line' || chartName === 'area';
var isFirstTick = shouldUseEdgeAlignment && payload.index === 0;
var isLastTick = shouldUseEdgeAlignment && payload.index === lastTick;
var getTextAnchor = function getTextAnchor() {
if (isFirstTick) return 'start';
if (isLastTick) return 'end';
return 'middle';
};
var textAnchor = getTextAnchor();
// Common text style props
var textStyleProps = {
maxWidth: maxWidth,
fontSize: fontSize,
lineHeight: X_AXIS_TICK_LINE_HEIGHT,
startDy: X_AXIS_TICK_START_DY,
textAnchor: textAnchor,
fill: theme.colors.surface.text.gray.muted,
fontFamily: theme.typography.fonts.family.text,
fontWeight: theme.typography.fonts.weight.regular,
letterSpacing: theme.typography.letterSpacings[100]
};
// Apply tickFormatter if provided, otherwise use raw value
var displayValue = tickFormatter ? tickFormatter(payload.value, payload.index) : String(payload.value);
// Primary label
var primaryLabel = wrappedTextLabel(_objectSpread(_objectSpread({}, textStyleProps), {}, {
text: displayValue
}));
// Secondary label from pre-computed map
var secondaryValue = secondaryLabelMap === null || secondaryLabelMap === void 0 ? void 0 : secondaryLabelMap[payload.index];
var secondaryLabel = secondaryValue !== undefined ? wrappedTextLabel(_objectSpread(_objectSpread({}, textStyleProps), {}, {
text: String(secondaryValue),
// primaryLabel.height gives us where primary ends (relative to its startDy)
// X_AXIS_LABEL_GAP adds the desired spacing between labels
yOffset: primaryLabel.height + X_AXIS_LABEL_GAP
})) : null;
// Calculate total tick height and report it
var totalTickHeight = X_AXIS_TICK_START_DY + primaryLabel.height + (secondaryLabel ? X_AXIS_LABEL_GAP + secondaryLabel.height : 0);
React__default.useEffect(function () {
onHeightCalculated === null || onHeightCalculated === void 0 || onHeightCalculated(totalTickHeight);
}, [totalTickHeight, onHeightCalculated]);
return /*#__PURE__*/jsxs("g", {
transform: "translate(".concat(x, ",").concat(y, ")"),
children: [primaryLabel.element, secondaryLabel === null || secondaryLabel === void 0 ? void 0 : secondaryLabel.element]
});
};
var _ChartXAxis = function _ChartXAxis(_ref3) {
var _ref3$interval = _ref3.interval,
interval = _ref3$interval === void 0 ? 0 : _ref3$interval,
_ref3$tickLine = _ref3.tickLine,
tickLine = _ref3$tickLine === void 0 ? false : _ref3$tickLine,
_label = _ref3.label,
dataKey = _ref3.dataKey,
height = _ref3.height,
tickFormatter = _ref3.tickFormatter,
props = _objectWithoutProperties(_ref3, _excluded);
var _useTheme = useTheme(),
theme = _useTheme.theme;
var _useCommonChartCompon = useCommonChartComponentsContext(),
secondaryLabelMap = _useCommonChartCompon.secondaryLabelMap,
chartName = _useCommonChartCompon.chartName,
dataLength = _useCommonChartCompon.dataLength;
// We don't want to pass secondaryDataKey to recharts
var _unusedsecondaryDataKey = props.secondaryDataKey,
restProps = _objectWithoutProperties(props, _excluded2);
// Calculate tick count from dataLength
var totalTickCount = dataLength !== null && dataLength !== void 0 ? dataLength : 1;
// Calculate visible tick count based on interval
// When interval is a number: 0 = show all, 1 = every 2nd, 2 = every 3rd, etc.
var visibleTickCount = typeof interval === 'number' ? Math.ceil(totalTickCount / (interval + 1)) : totalTickCount; // For string intervals like 'preserveStart', use total as fallback
var lastTick = totalTickCount - 1;
// State to track the maximum tick height reported by CustomXAxisTick components
var minHeight = secondaryLabelMap ? 20 : 10;
var _React$useState = React__default.useState(minHeight),
_React$useState2 = _slicedToArray(_React$useState, 2),
maxTickHeight = _React$useState2[0],
setMaxTickHeight = _React$useState2[1];
// Callback to update max height when ticks report their calculated height
var handleHeightCalculated = React__default.useCallback(function (height) {
setMaxTickHeight(function (prev) {
return Math.max(prev, height);
});
}, []);
// Calculate total axis height:
// - Tick labels height (dynamic)
// - X-axis label height + offset (if label prop is present)
var hasAxisLabel = Boolean(_label);
var axisLabelSpace = hasAxisLabel ? X_AXIS_LABEL_OFFSET + X_AXIS_LABEL_HEIGHT : 0;
var baseHeight = Math.max(maxTickHeight) + axisLabelSpace;
// Position for X-axis label: below tick labels with offset
var axisLabelY = maxTickHeight + X_AXIS_LABEL_OFFSET + X_AXIS_LABEL_HEIGHT / 2;
return /*#__PURE__*/jsx(XAxis, _objectSpread(_objectSpread({}, restProps), {}, {
height: baseHeight || height,
interval: interval,
tick: function tick(tickProps) {
var _tickProps$width;
// Calculate available width per tick from the total chart width
var tickWidth = Number((_tickProps$width = tickProps.width) !== null && _tickProps$width !== void 0 ? _tickProps$width : 0) / visibleTickCount;
return /*#__PURE__*/jsx(CustomXAxisTick, {
x: Number(tickProps.x),
y: Number(tickProps.y),
payload: tickProps.payload,
secondaryLabelMap: secondaryLabelMap,
theme: theme,
tickWidth: tickWidth,
lastTick: lastTick,
chartName: chartName,
onHeightCalculated: handleHeightCalculated,
tickFormatter: tickFormatter
});
},
tickLine: tickLine,
stroke: theme.colors.surface.border.gray.muted,
label: function label(_ref4) {
var viewBox = _ref4.viewBox;
return /*#__PURE__*/jsx("text", {
x: viewBox.x + viewBox.width / 2 - X_OFFSET,
y: viewBox.y + axisLabelY,
textAnchor: "middle",
fill: theme.colors.surface.text.gray.muted,
fontSize: theme.typography.fonts.size[75],
fontFamily: theme.typography.fonts.family.text,
fontWeight: theme.typography.fonts.weight.regular,
letterSpacing: theme.typography.letterSpacings[100],
children: _label
});
},
dataKey: dataKey
}));
};
var ChartXAxis = /*#__PURE__*/assignWithoutSideEffects(_ChartXAxis, {
componentId: componentId.chartXAxis
});
var ChartYAxis = function ChartYAxis(props) {
var _useTheme2 = useTheme(),
theme = _useTheme2.theme;
return /*#__PURE__*/jsx(YAxis, _objectSpread(_objectSpread({}, props), {}, {
tick: {
fill: theme.colors.surface.text.gray.muted,
fontSize: theme.typography.fonts.size[75],
fontFamily: theme.typography.fonts.family.text,
fontWeight: theme.typography.fonts.weight.regular,
letterSpacing: theme.typography.letterSpacings[100]
},
tickLine: false,
stroke: theme.colors.surface.border.gray.muted,
label: {
value: props === null || props === void 0 ? void 0 : props.label,
position: 'insideLeft',
style: {
textAnchor: 'middle',
fill: theme.colors.surface.text.gray.muted,
fontSize: theme.typography.fonts.size[75],
fontWeight: theme.typography.fonts.weight.regular,
fontFamily: theme.typography.fonts.family.text,
letterSpacing: theme.typography.letterSpacings[100],
lineHeight: theme.typography.lineHeights[500]
},
angle: -90,
fill: theme.colors.surface.text.gray.subtle
},
dataKey: props === null || props === void 0 ? void 0 : props.dataKey
}));
};
var ChartCartesianGrid = function ChartCartesianGrid(props) {
var _useTheme3 = useTheme(),
theme = _useTheme3.theme;
return /*#__PURE__*/jsx(CartesianGrid, _objectSpread({
stroke: theme.colors.surface.border.gray.muted,
vertical: false
}, props));
};
var CustomTooltip = function CustomTooltip(_ref5) {
var item = _ref5.item;
var _useTheme4 = useTheme(),
theme = _useTheme4.theme;
var _useCommonChartCompon2 = useCommonChartComponentsContext(),
dataColorMapping = _useCommonChartCompon2.dataColorMapping,
chartName = _useCommonChartCompon2.chartName;
var toolTipColor = getChartColor(item.dataKey, item.name, dataColorMapping !== null && dataColorMapping !== void 0 ? dataColorMapping : {}, chartName);
return /*#__PURE__*/jsxs(Box, {
display: "flex",
alignItems: "center",
justifyContent: "space-between",
gap: "spacing.4",
children: [/*#__PURE__*/jsxs(Box, {
display: "flex",
gap: "spacing.3",
alignItems: "center",
justifyContent: "center",
children: [/*#__PURE__*/jsx("div", {
style: {
width: theme.spacing[4],
height: theme.spacing[4],
backgroundColor: getIn(theme.colors, toolTipColor),
borderRadius: theme.border.radius.small
}
}), /*#__PURE__*/jsx(Text, {
size: "small",
weight: "regular",
color: "surface.text.staticWhite.normal",
children: item.name
})]
}), /*#__PURE__*/jsx(Text, {
size: "small",
weight: "regular",
color: "surface.text.staticWhite.normal",
children: item.value
})]
}, "tooltip-".concat(item.name));
};
var ChartTooltip = function ChartTooltip(props) {
var _useTheme5 = useTheme(),
theme = _useTheme5.theme;
return /*#__PURE__*/jsx(Tooltip, _objectSpread({
content: function content(_ref6) {
var payload = _ref6.payload,
label = _ref6.label;
var filteredPayLoad = payload.filter(function (item) {
return item.type !== 'none';
});
return /*#__PURE__*/jsxs("div", {
style: {
backgroundColor: theme.colors.surface.icon.staticBlack.normal,
borderRadius: theme.border.radius.large,
border: "1px solid ".concat(theme.colors.surface.border.gray.muted),
padding: theme.spacing[4]
},
children: [/*#__PURE__*/jsx(Heading, {
size: "small",
weight: "semibold",
color: "surface.text.staticWhite.normal",
children: label
}), /*#__PURE__*/jsx(Box, {
paddingTop: label ? 'spacing.4' : undefined,
children: filteredPayLoad.map(function (item) {
return /*#__PURE__*/jsx(CustomTooltip, {
item: item
}, item.name);
})
})]
});
},
cursor: {
fill: 'transparent',
stroke: 'transparent'
}
}, props));
};
var StyledLegendWrapper = /*#__PURE__*/styled.button.withConfig({
displayName: "CommonChartComponentsweb__StyledLegendWrapper",
componentId: "kmddnd-0"
})(function (_ref7) {
var theme = _ref7.theme,
$isHidden = _ref7.$isHidden,
$isClickable = _ref7.$isClickable;
return {
display: 'flex',
alignItems: 'center',
cursor: $isClickable ? 'pointer' : 'default',
opacity: $isHidden ? 0.4 : 1,
background: 'none',
border: 'none',
padding: 0,
'& p': {
color: theme.colors.surface.text.gray.muted,
transition: $isClickable ? "color ".concat(theme.motion.duration.xquick, "ms ").concat(theme.motion.easing.linear) : 'none'
},
'&:hover p': {
color: $isClickable ? theme.colors.surface.text.gray.normal : theme.colors.surface.text.gray.muted
}
};
});
var LegendItem = function LegendItem(_ref8) {
var _entry$value;
var entry = _ref8.entry,
index = _ref8.index,
isSelected = _ref8.isSelected,
onClick = _ref8.onClick,
isClickable = _ref8.isClickable;
var _useTheme6 = useTheme(),
theme = _useTheme6.theme;
var _useCommonChartCompon3 = useCommonChartComponentsContext(),
dataColorMapping = _useCommonChartCompon3.dataColorMapping,
chartName = _useCommonChartCompon3.chartName;
var legendColor = getChartColor(entry.dataKey, entry.value, dataColorMapping !== null && dataColorMapping !== void 0 ? dataColorMapping : {}, chartName);
// For donut charts, use sanitized value (name) as the key, for other charts use dataKey
var legendKey = chartName === 'donut' ? sanitizeString((_entry$value = entry.value) !== null && _entry$value !== void 0 ? _entry$value : '') : entry.dataKey;
return /*#__PURE__*/jsx(StyledLegendWrapper, {
$isHidden: !isSelected,
$isClickable: isClickable,
onClick: isClickable ? function () {
onClick(legendKey);
} : undefined,
type: "button",
children: /*#__PURE__*/jsxs(Box, {
display: "flex",
gap: "spacing.3",
justifyContent: "center",
alignItems: "center",
children: [/*#__PURE__*/jsx("span", {
style: {
backgroundColor: getIn(theme.colors, legendColor),
width: theme.spacing[4],
height: theme.spacing[4],
display: 'inline-block',
borderRadius: theme.border.radius.small
}
}), /*#__PURE__*/jsx(Text, {
size: "medium",
color: "surface.text.gray.muted",
children: entry.value
})]
})
}, "item-".concat(index));
};
var CustomSquareLegend = function CustomSquareLegend(props) {
var payload = props.payload,
layout = props.layout,
selectedDataKeys = props.selectedDataKeys,
onClick = props.onClick;
var _useCommonChartCompon4 = useCommonChartComponentsContext(),
chartName = _useCommonChartCompon4.chartName,
dataColorMapping = _useCommonChartCompon4.dataColorMapping;
/*
This is a custom legend component that is used to display the legend for the chart.
we need to show the legend only if the legendType is not none. (for example in line chart we don't want to show the legend for the reference line)
so we are filtering the payload and then mapping over it to display the legend.
For donut charts, we use dataColorMapping directly instead of payload because:
- When legend items are clicked, the donut data gets filtered
- This causes the payload to lose the filtered items
- But we need all legend items to remain visible so users can toggle them back
- dataColorMapping always contains all the original data keys
*/
var isVerticalLayout = layout === 'vertical';
var isClickable = chartName === 'line' || chartName === 'area' || chartName === 'bar' || chartName === 'donut';
// For donut charts, generate legend entries from dataColorMapping (which has all keys)
if (chartName === 'donut' && dataColorMapping) {
var donutLegendEntries = Object.keys(dataColorMapping).map(function (key) {
return {
value: key,
// The sanitized name
dataKey: key,
color: dataColorMapping[key].colorToken
};
});
if (donutLegendEntries.length === 0) {
return null;
}
return /*#__PURE__*/jsx(Box, {
display: "flex",
justifyContent: "center",
gap: "spacing.5",
flexDirection: isVerticalLayout ? 'column' : 'row',
width: isVerticalLayout ? '100%' : 'auto',
flexWrap: "wrap",
children: donutLegendEntries.map(function (entry, index) {
return /*#__PURE__*/jsx(LegendItem, {
entry: entry,
index: index,
isSelected: selectedDataKeys.includes(entry.dataKey),
onClick: onClick,
isClickable: isClickable
}, "item-".concat(index));
})
});
}
// For other chart types, use the payload from recharts
if (!payload || payload.length === 0) {
return null;
}
var filteredPayload = payload.filter(function (entry) {
var _entry$payload;
return (entry === null || entry === void 0 || (_entry$payload = entry.payload) === null || _entry$payload === void 0 ? void 0 : _entry$payload.legendType) !== 'none' && (entry === null || entry === void 0 ? void 0 : entry.type) !== 'none';
});
return /*#__PURE__*/jsx(Box, {
display: "flex",
justifyContent: "center",
gap: "spacing.5",
flexDirection: isVerticalLayout ? 'column' : 'row',
width: isVerticalLayout ? '100%' : 'auto',
flexWrap: "wrap",
children: filteredPayload.map(function (entry, index) {
return /*#__PURE__*/jsx(LegendItem, {
entry: entry,
index: index,
isSelected: selectedDataKeys.includes(entry.dataKey),
onClick: onClick,
isClickable: isClickable
}, "item-".concat(index));
})
});
};
var _ChartLegend = function _ChartLegend(_ref9) {
var _props$layout;
var selectedDataKeysProp = _ref9.selectedDataKeys,
defaultSelectedDataKeys = _ref9.defaultSelectedDataKeys,
onSelectedDataKeysChange = _ref9.onSelectedDataKeysChange,
props = _objectWithoutProperties(_ref9, _excluded3);
var _useTheme7 = useTheme(),
theme = _useTheme7.theme;
var _useCommonChartCompon5 = useCommonChartComponentsContext(),
dataColorMapping = _useCommonChartCompon5.dataColorMapping,
setSelectedDataKeys = _useCommonChartCompon5.setSelectedDataKeys;
// Get all available dataKeys from the chart
var allDataKeys = React__default.useMemo(function () {
return Object.keys(dataColorMapping !== null && dataColorMapping !== void 0 ? dataColorMapping : {});
}, [dataColorMapping]);
// Use controllable state for selected keys
var _useControllableState = useControllableState({
value: selectedDataKeysProp,
defaultValue: defaultSelectedDataKeys !== null && defaultSelectedDataKeys !== void 0 ? defaultSelectedDataKeys : allDataKeys
}),
_useControllableState2 = _slicedToArray(_useControllableState, 2),
selectedKeysArray = _useControllableState2[0],
setSelectedKeysArray = _useControllableState2[1];
// Reset selection when allDataKeys completely changes (e.g., when nameKey prop changes)
// This detects when none of the currently selected keys exist in the new data keys
React__default.useEffect(function () {
if (allDataKeys.length > 0 && selectedKeysArray.length > 0) {
var hasOverlap = selectedKeysArray.some(function (key) {
return allDataKeys.includes(key);
});
if (!hasOverlap) {
// The data keys have completely changed, reset to all keys
setSelectedKeysArray(function () {
return _toConsumableArray(allDataKeys);
});
}
}
}, [allDataKeys, selectedKeysArray, setSelectedKeysArray]);
// Sync selectedDataKeys to context's selectedDataKeys
React__default.useEffect(function () {
setSelectedDataKeys === null || setSelectedDataKeys === void 0 || setSelectedDataKeys(selectedKeysArray);
}, [selectedKeysArray, setSelectedDataKeys]);
// Handle toggle
var handleClick = React__default.useCallback(function (dataKey) {
var newSelectedKeys = selectedKeysArray.includes(dataKey) ? selectedKeysArray.filter(function (key) {
return key !== dataKey;
}) : [].concat(_toConsumableArray(selectedKeysArray), [dataKey]);
setSelectedKeysArray(function () {
return newSelectedKeys;
});
onSelectedDataKeysChange === null || onSelectedDataKeysChange === void 0 || onSelectedDataKeysChange({
dataKey: dataKey,
selectedKeysArray: newSelectedKeys
});
}, [setSelectedKeysArray, selectedKeysArray, onSelectedDataKeysChange]);
return /*#__PURE__*/jsx(Legend, _objectSpread({
wrapperStyle: {
fontFamily: theme.typography.fonts.family.text,
fontSize: theme.typography.fonts.size[100],
color: theme.colors.surface.text.gray.normal,
paddingTop: LEGEND_MARGIN_TOP
},
align: "center",
verticalAlign: props.layout === 'vertical' ? 'middle' : 'bottom',
content: /*#__PURE__*/jsx(CustomSquareLegend, {
layout: (_props$layout = props.layout) !== null && _props$layout !== void 0 ? _props$layout : 'horizontal',
selectedDataKeys: selectedKeysArray,
onClick: handleClick
})
}, props));
};
var ChartLegend = /*#__PURE__*/assignWithoutSideEffects(_ChartLegend, {
componentId: componentId.chartLegend
});
var CustomReferenceLabel = function CustomReferenceLabel(_ref0) {
var viewBox = _ref0.viewBox,
value = _ref0.value,
isVertical = _ref0.isVertical;
// Extract viewBox coordinates with fallback values to prevent undefined errors.
// viewBox contains the positioning information for the reference line label from Recharts.
var _ref1 = viewBox !== null && viewBox !== void 0 ? viewBox : {
x: 0,
y: 0,
width: 0
},
x = _ref1.x,
y = _ref1.y,
width = _ref1.width;
var _useTheme8 = useTheme(),
theme = _useTheme8.theme;
// Calculate dynamic text width to ensure the background rectangle fits the text perfectly.
// This prevents text overflow for long labels and avoids unnecessarily large rectangles for short text.
// The function also handles text truncation with ellipsis if the text exceeds the maximum width.
var _ref10 = value ? calculateTextWidth(value, theme) : {
width: 80,
displayText: value !== null && value !== void 0 ? value : ''
},
RECT_WIDTH = _ref10.width,
displayText = _ref10.displayText;
var rect_x = isVertical ? x + width - RECT_WIDTH / 2 : x + width - RECT_WIDTH;
var rect_y = isVertical ? y : y - TEXT_BASELINE;
// Text position with padding inside the rectangle
var text_x = rect_x + PADDING_HORIZONTAL + (RECT_WIDTH - PADDING_HORIZONTAL * 2) / 2;
var text_y = rect_y + PADDING_VERTICAL + TEXT_BASELINE; // +15 for text baseline
return /*#__PURE__*/jsxs("g", {
children: [/*#__PURE__*/jsx("rect", {
x: rect_x,
y: rect_y,
width: RECT_WIDTH,
height: RECT_HEIGHT,
rx: theme.border.radius.medium,
fill: theme.colors.surface.background.gray.subtle,
stroke: theme.colors.surface.border.gray.muted,
strokeWidth: "1"
}), /*#__PURE__*/jsx("text", {
x: text_x,
y: text_y,
textAnchor: "middle",
fill: theme.colors.surface.text.gray.normal,
fontSize: theme.typography.fonts.size[50],
fontFamily: theme.typography.fonts.family.text,
fontWeight: theme.typography.fonts.weight.medium,
letterSpacing: theme.typography.letterSpacings[100],
children: displayText
})]
});
};
var ChartReferenceLine = function ChartReferenceLine(_ref11) {
var label = _ref11.label,
x = _ref11.x,
y = _ref11.y;
var _useTheme9 = useTheme(),
theme = _useTheme9.theme;
return /*#__PURE__*/jsx(ReferenceLine, {
stroke: theme.colors.data.background.categorical.gray.intense,
strokeWidth: 2,
strokeDasharray: "4 4",
label: /*#__PURE__*/jsx(CustomReferenceLabel, {
value: label,
isVertical: Boolean(x)
}),
x: x,
y: y
});
};
export { ChartCartesianGrid, ChartLegend, ChartReferenceLine, ChartTooltip, ChartXAxis, ChartYAxis };
//# sourceMappingURL=CommonChartComponents.web.js.map