UNPKG

@razorpay/blade

Version:

The Design System that powers Razorpay

738 lines (705 loc) 32 kB
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