@automattic/charts 4.11.0 → 4.12.0
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/CHANGELOG.md +18 -0
- package/dist/index.cjs +856 -236
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +19 -13
- package/dist/index.d.cts +43 -4
- package/dist/index.d.ts +43 -4
- package/dist/index.js +855 -237
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
- package/src/charts/bar-chart/bar-chart.tsx +134 -138
- package/src/charts/bar-chart/index.ts +3 -0
- package/src/charts/bar-chart/private/band-tooltip.tsx +41 -15
- package/src/charts/bar-chart/private/bar-layer.tsx +132 -0
- package/src/charts/bar-chart/private/comparison-bars-geometry.ts +22 -34
- package/src/charts/bar-chart/private/comparison-bars.tsx +9 -18
- package/src/charts/bar-chart/private/comparison-constants.ts +4 -10
- package/src/charts/bar-chart/private/grouped-bars.ts +68 -0
- package/src/charts/bar-chart/private/index.ts +7 -1
- package/src/charts/bar-chart/private/overlap-bars.ts +183 -0
- package/src/charts/bar-chart/private/selected-bar-ring.tsx +84 -0
- package/src/charts/bar-chart/private/slot-tooltip-members.tsx +29 -0
- package/src/charts/bar-chart/private/slotted-bar-series.tsx +84 -0
- package/src/charts/bar-chart/private/test/comparison-bars-geometry.test.ts +4 -11
- package/src/charts/bar-chart/private/test/comparison-bars.test.tsx +2 -3
- package/src/charts/bar-chart/private/test/grouped-bars.test.ts +87 -0
- package/src/charts/bar-chart/private/test/overlap-bars.test.ts +159 -0
- package/src/charts/bar-chart/private/use-bar-stops.ts +89 -0
- package/src/charts/bar-chart/private/use-overlap-slots.ts +194 -0
- package/src/charts/bar-chart/private/value-domain.ts +0 -10
- package/src/charts/bar-chart/test/bar-chart-overlap.test.tsx +347 -0
- package/src/charts/bar-chart/test/bar-chart.test.tsx +66 -19
- package/src/charts/bar-chart/test/use-bar-legend-items.test.tsx +81 -0
- package/src/charts/bar-chart/types.ts +3 -0
- package/src/charts/bar-chart/use-bar-colors.ts +46 -0
- package/src/charts/bar-chart/use-bar-legend-items.ts +87 -0
- package/src/charts/line-chart/line-chart.tsx +22 -17
- package/src/charts/line-chart/test/line-chart.test.tsx +165 -1
- package/src/charts/pie-chart/pie-chart.tsx +7 -2
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +7 -2
- package/src/charts/private/pie-keyboard-navigation/index.ts +0 -1
- package/src/charts/private/{pie-keyboard-navigation/pie-keyboard-navigation.module.scss → selected-ring/selected-ring.module.scss} +11 -6
- package/src/charts/private/selected-ring/selected-ring.tsx +73 -0
- package/src/charts/private/{pie-keyboard-navigation/test/selected-segment-ring.test.ts → selected-ring/test/selected-ring.test.ts} +1 -1
- package/src/components/legend/hooks/test/use-chart-legend-items.test.tsx +20 -0
- package/src/components/legend/hooks/use-chart-legend-items.ts +27 -3
- package/src/components/tooltip/xy-chart-tooltip.tsx +3 -2
- package/src/index.ts +8 -2
- package/src/providers/chart-context/global-charts-provider.tsx +2 -0
- package/src/providers/chart-context/test/chart-context.test.tsx +0 -13
- package/src/providers/chart-context/themes.ts +0 -1
- package/src/providers/chart-context/types.ts +2 -0
- package/src/test-utils/selected-bar-ring.ts +14 -0
- package/src/types.ts +10 -2
- package/src/charts/private/pie-keyboard-navigation/selected-segment-ring.tsx +0 -47
package/dist/index.cjs
CHANGED
|
@@ -1298,10 +1298,7 @@ const defaultTheme = {
|
|
|
1298
1298
|
strokeDasharray: "4 4",
|
|
1299
1299
|
strokeLinecap: "square"
|
|
1300
1300
|
} } },
|
|
1301
|
-
barChart: { barStyles: { comparison: {
|
|
1302
|
-
widthFactor: 1.5,
|
|
1303
|
-
opacity: .5
|
|
1304
|
-
} } },
|
|
1301
|
+
barChart: { barStyles: { comparison: { widthFactor: 1.5 } } },
|
|
1305
1302
|
sparkline: {
|
|
1306
1303
|
margin: {
|
|
1307
1304
|
top: 2,
|
|
@@ -1459,6 +1456,7 @@ const GlobalChartsProvider = ({ children, theme, locale, timeZone }) => {
|
|
|
1459
1456
|
theme: providerTheme,
|
|
1460
1457
|
formatting,
|
|
1461
1458
|
getElementStyles,
|
|
1459
|
+
backgroundColor: colorCache.background,
|
|
1462
1460
|
toggleSeriesVisibility,
|
|
1463
1461
|
setSeriesVisibility,
|
|
1464
1462
|
setChartHiddenSeries,
|
|
@@ -1475,6 +1473,7 @@ const GlobalChartsProvider = ({ children, theme, locale, timeZone }) => {
|
|
|
1475
1473
|
providerTheme,
|
|
1476
1474
|
formatting,
|
|
1477
1475
|
getElementStyles,
|
|
1476
|
+
colorCache.background,
|
|
1478
1477
|
toggleSeriesVisibility,
|
|
1479
1478
|
setSeriesVisibility,
|
|
1480
1479
|
setChartHiddenSeries,
|
|
@@ -2299,6 +2298,21 @@ const Legend = (0, react.forwardRef)(({ chartId, items, shape, ...props }, ref)
|
|
|
2299
2298
|
//#endregion
|
|
2300
2299
|
//#region src/components/legend/hooks/use-chart-legend-items.ts
|
|
2301
2300
|
/**
|
|
2301
|
+
* Wraps `getElementStyles` so a swatch takes the color given for its series.
|
|
2302
|
+
* @param getElementStyles - Function to get element styles
|
|
2303
|
+
* @param seriesColors - Each series' swatch color, by index
|
|
2304
|
+
* @return Element styles with the given color
|
|
2305
|
+
*/
|
|
2306
|
+
function withSeriesColors(getElementStyles, seriesColors) {
|
|
2307
|
+
return (params) => {
|
|
2308
|
+
const styles = getElementStyles(params);
|
|
2309
|
+
return {
|
|
2310
|
+
...styles,
|
|
2311
|
+
color: seriesColors[params.index] ?? styles.color
|
|
2312
|
+
};
|
|
2313
|
+
};
|
|
2314
|
+
}
|
|
2315
|
+
/**
|
|
2302
2316
|
* Formats the value for a data point based on its type and display preference
|
|
2303
2317
|
* @param point - The data point to format
|
|
2304
2318
|
* @param showValues - Whether to show values or return empty string
|
|
@@ -2510,21 +2524,22 @@ function processPointData(pointData, getElementStyles, showValues, legendValueDi
|
|
|
2510
2524
|
* @return Array of legend items ready for display
|
|
2511
2525
|
*/
|
|
2512
2526
|
function useChartLegendItems(data, options = {}, legendShape) {
|
|
2513
|
-
const { showValues = false, legendValueDisplay = "percentage", withGlyph = false, glyphSize = 8, collapseGroups = false, comparisonItem = false, comparisonSeriesItems, renderGlyph } = options;
|
|
2514
|
-
const { getElementStyles } = useGlobalChartsContext();
|
|
2527
|
+
const { showValues = false, legendValueDisplay = "percentage", withGlyph = false, glyphSize = 8, collapseGroups = false, comparisonItem = false, comparisonSeriesItems, seriesColors, renderGlyph } = options;
|
|
2528
|
+
const { getElementStyles: getSeriesStyles } = useGlobalChartsContext();
|
|
2515
2529
|
return (0, react.useMemo)(() => {
|
|
2516
2530
|
if (!data || !Array.isArray(data) || data.length === 0) return [];
|
|
2517
2531
|
if ("data" in data[0]) {
|
|
2518
2532
|
const seriesData = data;
|
|
2533
|
+
const getElementStyles = seriesColors ? withSeriesColors(getSeriesStyles, seriesColors) : getSeriesStyles;
|
|
2519
2534
|
const items = processSeriesData(seriesData, getElementStyles, showValues, withGlyph, glyphSize, collapseGroups, renderGlyph, legendShape);
|
|
2520
2535
|
if (comparisonSeriesItems) return [...items, ...buildPerSeriesComparisonItems(seriesData, items, comparisonSeriesItems, getElementStyles, legendShape)];
|
|
2521
2536
|
const comparison = comparisonItem ? buildComparisonLegendItem(seriesData, items, typeof comparisonItem === "string" ? comparisonItem : (0, _wordpress_i18n.__)("Comparison period", "jetpack-charts"), getElementStyles, legendShape) : null;
|
|
2522
2537
|
return comparison ? [...items, comparison] : items;
|
|
2523
2538
|
}
|
|
2524
|
-
return processPointData(data,
|
|
2539
|
+
return processPointData(data, getSeriesStyles, showValues, legendValueDisplay, withGlyph, glyphSize, renderGlyph, legendShape);
|
|
2525
2540
|
}, [
|
|
2526
2541
|
data,
|
|
2527
|
-
|
|
2542
|
+
getSeriesStyles,
|
|
2528
2543
|
showValues,
|
|
2529
2544
|
legendValueDisplay,
|
|
2530
2545
|
withGlyph,
|
|
@@ -2532,6 +2547,7 @@ function useChartLegendItems(data, options = {}, legendShape) {
|
|
|
2532
2547
|
collapseGroups,
|
|
2533
2548
|
comparisonItem,
|
|
2534
2549
|
comparisonSeriesItems,
|
|
2550
|
+
seriesColors,
|
|
2535
2551
|
renderGlyph,
|
|
2536
2552
|
legendShape
|
|
2537
2553
|
]);
|
|
@@ -3057,17 +3073,19 @@ const XYChartTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardFocu
|
|
|
3057
3073
|
return;
|
|
3058
3074
|
}
|
|
3059
3075
|
hasKeyboardSelection.current = true;
|
|
3060
|
-
if (mode === "group")
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
datum
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3076
|
+
if (mode === "group") {
|
|
3077
|
+
closeTooltipNow();
|
|
3078
|
+
series.forEach((s, index) => {
|
|
3079
|
+
if (selectedIndex < s.data.length) {
|
|
3080
|
+
const datum = s.data[selectedIndex];
|
|
3081
|
+
tooltipContext?.showTooltip({
|
|
3082
|
+
datum,
|
|
3083
|
+
key: s.label,
|
|
3084
|
+
index
|
|
3085
|
+
});
|
|
3086
|
+
}
|
|
3087
|
+
});
|
|
3088
|
+
} else if (mode === "individual") {
|
|
3071
3089
|
if (selectedIndex < tooltipData.length) {
|
|
3072
3090
|
const tooltipItem = tooltipData[selectedIndex];
|
|
3073
3091
|
tooltipContext?.showTooltip({
|
|
@@ -4814,6 +4832,8 @@ const LineChartInternal = (0, react.forwardRef)(({ data, ariaLabel, chartId: pro
|
|
|
4814
4832
|
isVisible: !hiddenSeries.has(series.label)
|
|
4815
4833
|
}));
|
|
4816
4834
|
}, [dataSorted, hiddenSeries]);
|
|
4835
|
+
const visibleSeries = (0, react.useMemo)(() => dataSorted.filter((series) => isSeriesVisible(series.label)), [dataSorted, isSeriesVisible]);
|
|
4836
|
+
const visibleSeriesKey = (0, react.useMemo)(() => JSON.stringify(visibleSeries.map((series) => series.label)), [visibleSeries]);
|
|
4817
4837
|
const allSeriesHidden = (0, react.useMemo)(() => {
|
|
4818
4838
|
return seriesWithVisibility.every(({ isVisible }) => !isVisible);
|
|
4819
4839
|
}, [seriesWithVisibility]);
|
|
@@ -4844,21 +4864,21 @@ const LineChartInternal = (0, react.forwardRef)(({ data, ariaLabel, chartId: pro
|
|
|
4844
4864
|
options?.yScale
|
|
4845
4865
|
]);
|
|
4846
4866
|
const activateSelectedPoint = (0, react.useCallback)((index) => {
|
|
4847
|
-
const series =
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
datum,
|
|
4867
|
+
const series = visibleSeries.find((candidate) => candidate.data[index]);
|
|
4868
|
+
if (series) onDatumActivate?.({
|
|
4869
|
+
datum: series.data[index],
|
|
4851
4870
|
index,
|
|
4852
4871
|
key: series.label
|
|
4853
4872
|
});
|
|
4854
|
-
}, [
|
|
4873
|
+
}, [visibleSeries, onDatumActivate]);
|
|
4855
4874
|
const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
|
|
4856
4875
|
selectedIndex,
|
|
4857
4876
|
setSelectedIndex,
|
|
4858
4877
|
isNavigating,
|
|
4859
4878
|
setIsNavigating,
|
|
4860
4879
|
chartRef,
|
|
4861
|
-
totalPoints:
|
|
4880
|
+
totalPoints: Math.max(0, ...visibleSeries.map((series) => series.data.length)),
|
|
4881
|
+
visibleSeriesKey,
|
|
4862
4882
|
onActivate: activateSelectedPoint,
|
|
4863
4883
|
preventTooltipScroll: tooltipPlacement === "below-axis"
|
|
4864
4884
|
});
|
|
@@ -4877,7 +4897,7 @@ const LineChartInternal = (0, react.forwardRef)(({ data, ariaLabel, chartId: pro
|
|
|
4877
4897
|
}
|
|
4878
4898
|
return min <= max ? [min, max] : void 0;
|
|
4879
4899
|
}, [dataSorted, isSeriesVisible]);
|
|
4880
|
-
const hasWholeNumberValues = (0, react.useMemo)(() => hasOnlyWholeNumbers(
|
|
4900
|
+
const hasWholeNumberValues = (0, react.useMemo)(() => hasOnlyWholeNumbers(visibleSeries), [visibleSeries]);
|
|
4881
4901
|
const chartOptions = (0, react.useMemo)(() => {
|
|
4882
4902
|
const fallbackYDomain = getFallbackYDomain(visibleReadingExtent, options?.yScale?.type === "log");
|
|
4883
4903
|
return {
|
|
@@ -4923,11 +4943,7 @@ const LineChartInternal = (0, react.forwardRef)(({ data, ariaLabel, chartId: pro
|
|
|
4923
4943
|
formatting,
|
|
4924
4944
|
isSeriesVisible
|
|
4925
4945
|
]);
|
|
4926
|
-
const bucketInfo = (0, react.useMemo)(() => getBucketInfo(
|
|
4927
|
-
dataSorted,
|
|
4928
|
-
isSeriesVisible,
|
|
4929
|
-
options?.axis?.x?.tickResolution
|
|
4930
|
-
]);
|
|
4946
|
+
const bucketInfo = (0, react.useMemo)(() => getBucketInfo(visibleSeries, options?.axis?.x?.tickResolution), [visibleSeries, options?.axis?.x?.tickResolution]);
|
|
4931
4947
|
const tooltipRenderGlyph = (0, react.useMemo)(() => {
|
|
4932
4948
|
return (props) => {
|
|
4933
4949
|
const seriesIndex = dataSorted.findIndex((series) => series.label === props.key || series.data.includes(props.datum));
|
|
@@ -5157,7 +5173,7 @@ const LineChartInternal = (0, react.forwardRef)(({ data, ariaLabel, chartId: pro
|
|
|
5157
5173
|
selectedIndex,
|
|
5158
5174
|
tooltipRef,
|
|
5159
5175
|
keyboardFocusedClassName: line_chart_module_default["line-chart__tooltip--keyboard-focused"],
|
|
5160
|
-
series:
|
|
5176
|
+
series: visibleSeries
|
|
5161
5177
|
}),
|
|
5162
5178
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LineChartScalesRef, {
|
|
5163
5179
|
chartRef: internalChartRef,
|
|
@@ -5817,13 +5833,6 @@ const getBarValue = (point) => {
|
|
|
5817
5833
|
*/
|
|
5818
5834
|
const isBarRendered = (point) => Number.isFinite(getBarValue(point));
|
|
5819
5835
|
/**
|
|
5820
|
-
* How many bars visx renders for a list of points.
|
|
5821
|
-
*
|
|
5822
|
-
* @param points - Data points handed to visx.
|
|
5823
|
-
* @return The number of points that get a bar.
|
|
5824
|
-
*/
|
|
5825
|
-
const countRenderedBars = (points) => points.filter(isBarRendered).length;
|
|
5826
|
-
/**
|
|
5827
5836
|
* An explicit domain for the value scale, or null to let visx fit one to the data.
|
|
5828
5837
|
*
|
|
5829
5838
|
* Zero goes into the domain here rather than through the scale's `zero` flag: visx applies
|
|
@@ -6085,8 +6094,7 @@ const createGroupScale = (keys, bandwidth, padding) => (0, _visx_scale.scaleBand
|
|
|
6085
6094
|
//#endregion
|
|
6086
6095
|
//#region src/charts/bar-chart/private/comparison-bars-geometry.ts
|
|
6087
6096
|
/**
|
|
6088
|
-
* Mirrors visx's getScaleBaseline so
|
|
6089
|
-
* share the same baseline, including out-of-domain zero values.
|
|
6097
|
+
* Mirrors visx's getScaleBaseline so bars drawn outside visx share the baseline of its bars, including out-of-domain zero values.
|
|
6090
6098
|
*
|
|
6091
6099
|
* @param {ValueScale} scale - The continuous value scale.
|
|
6092
6100
|
* @return {number} The baseline output position in pixels.
|
|
@@ -6100,36 +6108,33 @@ function getValueScaleBaseline(scale) {
|
|
|
6100
6108
|
return Number.isFinite(maybeZero) ? Math.max(maybeZero, minOutput) : minOutput;
|
|
6101
6109
|
}
|
|
6102
6110
|
/**
|
|
6103
|
-
* Compute the rect
|
|
6104
|
-
*
|
|
6105
|
-
*
|
|
6106
|
-
* @param {
|
|
6107
|
-
* @param {
|
|
6108
|
-
* @param {number}
|
|
6109
|
-
* @param {number}
|
|
6110
|
-
* @param {number}
|
|
6111
|
-
* @param {number}
|
|
6112
|
-
* @
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
const
|
|
6118
|
-
const
|
|
6119
|
-
const
|
|
6120
|
-
const shadowStart = slotStart + slotThickness / 2 - shadowThickness / 2;
|
|
6121
|
-
const valueStart = Math.min(valuePosition, baseline);
|
|
6122
|
-
const valueLength = Math.abs(baseline - valuePosition);
|
|
6111
|
+
* Compute the rect that spans `valuePositions` along the value axis, centered on a slot and scaled by `widthFactor` across it.
|
|
6112
|
+
*
|
|
6113
|
+
* @param {object} params - Geometry inputs.
|
|
6114
|
+
* @param {boolean} params.horizontal - True for a horizontal bar chart, false for vertical.
|
|
6115
|
+
* @param {number} params.bandPosition - bandScale(category): start px of the category band.
|
|
6116
|
+
* @param {number} params.slotOffset - groupScale(slotKey): offset of the slot within the band.
|
|
6117
|
+
* @param {number} params.slotThickness - groupScale.bandwidth(): slot thickness in px.
|
|
6118
|
+
* @param {number[]} params.valuePositions - Output px the rect must reach, such as the baseline and each bar's value.
|
|
6119
|
+
* @param {number} params.widthFactor - Thickness multiplier, e.g. 0.5 for half the slot.
|
|
6120
|
+
* @return {SlotRect} The {x, y, width, height} of the rect.
|
|
6121
|
+
*/
|
|
6122
|
+
function computeSlotRect(params) {
|
|
6123
|
+
const { horizontal, bandPosition, slotOffset, slotThickness, valuePositions, widthFactor } = params;
|
|
6124
|
+
const thickness = slotThickness * widthFactor;
|
|
6125
|
+
const start = bandPosition + slotOffset + slotThickness / 2 - thickness / 2;
|
|
6126
|
+
const valueStart = Math.min(...valuePositions);
|
|
6127
|
+
const valueLength = Math.max(...valuePositions) - valueStart;
|
|
6123
6128
|
if (horizontal) return {
|
|
6124
6129
|
x: valueStart,
|
|
6125
|
-
y:
|
|
6130
|
+
y: start,
|
|
6126
6131
|
width: valueLength,
|
|
6127
|
-
height:
|
|
6132
|
+
height: thickness
|
|
6128
6133
|
};
|
|
6129
6134
|
return {
|
|
6130
|
-
x:
|
|
6135
|
+
x: start,
|
|
6131
6136
|
y: valueStart,
|
|
6132
|
-
width:
|
|
6137
|
+
width: thickness,
|
|
6133
6138
|
height: valueLength
|
|
6134
6139
|
};
|
|
6135
6140
|
}
|
|
@@ -6178,6 +6183,518 @@ const ClassifiedBarSeries = ({ barClassName, primaryKeys, groupPadding, ...props
|
|
|
6178
6183
|
renderGlyph
|
|
6179
6184
|
});
|
|
6180
6185
|
};
|
|
6186
|
+
//#endregion
|
|
6187
|
+
//#region src/charts/bar-chart/private/slotted-bar-series.tsx
|
|
6188
|
+
const SlottedBarSeries = ({ barClassName, slotKeys, slotKey, thickness, groupPadding, ...props }) => {
|
|
6189
|
+
const { xScale, yScale, horizontal } = (0, react.useContext)(_visx_xychart.DataContext);
|
|
6190
|
+
const bandScale = horizontal ? yScale : xScale;
|
|
6191
|
+
const valueScale = horizontal ? xScale : yScale;
|
|
6192
|
+
const groupScale = (0, react.useMemo)(() => createGroupScale(slotKeys, bandScale?.bandwidth?.() ?? 0, groupPadding), [
|
|
6193
|
+
slotKeys,
|
|
6194
|
+
bandScale,
|
|
6195
|
+
groupPadding
|
|
6196
|
+
]);
|
|
6197
|
+
const renderGlyph = (0, react.useCallback)(({ datum, x, y, color }) => {
|
|
6198
|
+
if (!bandScale?.bandwidth || !valueScale) return null;
|
|
6199
|
+
const baseline = getValueScaleBaseline(valueScale);
|
|
6200
|
+
const rect = computeSlotRect({
|
|
6201
|
+
horizontal: !!horizontal,
|
|
6202
|
+
bandPosition: Number(bandScale((horizontal ? props.yAccessor : props.xAccessor)(datum))),
|
|
6203
|
+
slotOffset: groupScale(slotKey) ?? 0,
|
|
6204
|
+
slotThickness: groupScale.bandwidth(),
|
|
6205
|
+
valuePositions: [baseline, horizontal ? x : y],
|
|
6206
|
+
widthFactor: thickness
|
|
6207
|
+
});
|
|
6208
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
6209
|
+
...rect,
|
|
6210
|
+
className: (0, clsx.default)("visx-bar", barClassName?.(datum)),
|
|
6211
|
+
fill: color
|
|
6212
|
+
});
|
|
6213
|
+
}, [
|
|
6214
|
+
bandScale,
|
|
6215
|
+
valueScale,
|
|
6216
|
+
horizontal,
|
|
6217
|
+
groupScale,
|
|
6218
|
+
slotKey,
|
|
6219
|
+
thickness,
|
|
6220
|
+
props.xAccessor,
|
|
6221
|
+
props.yAccessor,
|
|
6222
|
+
barClassName
|
|
6223
|
+
]);
|
|
6224
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.GlyphSeries, {
|
|
6225
|
+
...props,
|
|
6226
|
+
enableEvents: false,
|
|
6227
|
+
renderGlyph
|
|
6228
|
+
});
|
|
6229
|
+
};
|
|
6230
|
+
//#endregion
|
|
6231
|
+
//#region src/charts/private/selected-ring/selected-ring.module.scss
|
|
6232
|
+
var selected_ring_module_default = {
|
|
6233
|
+
"selected-ring__contrast": "a8ccharts-gx1_AW-selected-ring__contrast",
|
|
6234
|
+
"selected-ring__edge": "a8ccharts-gx1_AW-selected-ring__edge",
|
|
6235
|
+
"selected-ring__far": "a8ccharts-gx1_AW-selected-ring__far",
|
|
6236
|
+
"selected-ring__focus": "a8ccharts-gx1_AW-selected-ring__focus"
|
|
6237
|
+
};
|
|
6238
|
+
//#endregion
|
|
6239
|
+
//#region src/charts/private/selected-ring/selected-ring.tsx
|
|
6240
|
+
const TONES = {
|
|
6241
|
+
inside: ["contrast", "focus"],
|
|
6242
|
+
outside: ["focus", "contrast"]
|
|
6243
|
+
};
|
|
6244
|
+
const EVERYWHERE = "M-1e6 -1e6H1e6V1e6H-1e6Z";
|
|
6245
|
+
/**
|
|
6246
|
+
* Encodes `value` for an SVG id, losslessly so distinct chart ids never share a clip path.
|
|
6247
|
+
*
|
|
6248
|
+
* @param value - Any string.
|
|
6249
|
+
* @return `value` with each character outside `[A-Za-z0-9-]` written as `_<hex code point>_`.
|
|
6250
|
+
*/
|
|
6251
|
+
const toSvgId = (value) => Array.from(value, (character) => /[A-Za-z0-9-]/.test(character) ? character : `_${character.codePointAt(0)?.toString(16)}_`).join("");
|
|
6252
|
+
/**
|
|
6253
|
+
* Marks a selected shape with a focus-colored ring and a contrast ring, so it reads against any fill.
|
|
6254
|
+
*
|
|
6255
|
+
* @param {SelectedRingProps} props - Component props
|
|
6256
|
+
* @return {JSX.Element} The ring
|
|
6257
|
+
*/
|
|
6258
|
+
const SelectedRing = ({ chartId, d, side, testId }) => {
|
|
6259
|
+
const clipId = `selected-ring-clip-${toSvgId(chartId)}`;
|
|
6260
|
+
const clipPath = `url(#${clipId})`;
|
|
6261
|
+
const [far, edge] = TONES[side];
|
|
6262
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", {
|
|
6263
|
+
pointerEvents: "none",
|
|
6264
|
+
"aria-hidden": "true",
|
|
6265
|
+
children: [
|
|
6266
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("clipPath", {
|
|
6267
|
+
id: clipId,
|
|
6268
|
+
children: side === "inside" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
6269
|
+
d: `${EVERYWHERE} ${d}`,
|
|
6270
|
+
clipRule: "evenodd"
|
|
6271
|
+
})
|
|
6272
|
+
}),
|
|
6273
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
6274
|
+
d,
|
|
6275
|
+
clipPath,
|
|
6276
|
+
className: (0, clsx.default)(selected_ring_module_default["selected-ring__far"], selected_ring_module_default[`selected-ring__${far}`])
|
|
6277
|
+
}),
|
|
6278
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
6279
|
+
d,
|
|
6280
|
+
clipPath,
|
|
6281
|
+
className: (0, clsx.default)(selected_ring_module_default["selected-ring__edge"], selected_ring_module_default[`selected-ring__${edge}`])
|
|
6282
|
+
})
|
|
6283
|
+
]
|
|
6284
|
+
});
|
|
6285
|
+
};
|
|
6286
|
+
//#endregion
|
|
6287
|
+
//#region src/charts/bar-chart/private/selected-bar-ring.tsx
|
|
6288
|
+
/**
|
|
6289
|
+
* Marks the keyboard-selected slot with a two-tone ring outside its edge, so the ring reads against any bar color.
|
|
6290
|
+
*
|
|
6291
|
+
* @param props - Component props.
|
|
6292
|
+
* @param props.chartId - The chart's id, which keeps the clip path unique on a page with several charts.
|
|
6293
|
+
* @param props.selected - The selected slot.
|
|
6294
|
+
* @param props.slotKeys - The slots, in the order the band lays them out.
|
|
6295
|
+
* @param props.groupPadding - The padding between slots.
|
|
6296
|
+
* @param props.horizontal - Whether the bars run horizontally.
|
|
6297
|
+
* @param props.xAccessor - Reads a datum's x value.
|
|
6298
|
+
* @param props.yAccessor - Reads a datum's y value.
|
|
6299
|
+
* @return The ring, or nothing when the slot has no position.
|
|
6300
|
+
*/
|
|
6301
|
+
const SelectedBarRing = ({ chartId, selected, slotKeys, groupPadding, horizontal, xAccessor, yAccessor }) => {
|
|
6302
|
+
const context = (0, react.useContext)(_visx_xychart.DataContext);
|
|
6303
|
+
const bandScale = horizontal ? context?.yScale : context?.xScale;
|
|
6304
|
+
const valueScale = horizontal ? context?.xScale : context?.yScale;
|
|
6305
|
+
const bandwidth = bandScale?.bandwidth?.() ?? 0;
|
|
6306
|
+
const [category] = selected.data;
|
|
6307
|
+
if (!bandScale || !valueScale || !bandwidth || !category) return null;
|
|
6308
|
+
const groupScale = createGroupScale(slotKeys, bandwidth, groupPadding);
|
|
6309
|
+
const slotOffset = groupScale(selected.slotKey);
|
|
6310
|
+
const bandPosition = Number(bandScale((horizontal ? yAccessor : xAccessor)(category)));
|
|
6311
|
+
if (slotOffset == null || !Number.isFinite(bandPosition)) return null;
|
|
6312
|
+
const valuePositions = selected.data.map((datum) => Number(valueScale(Number((horizontal ? xAccessor : yAccessor)(datum)))));
|
|
6313
|
+
const rect = computeSlotRect({
|
|
6314
|
+
horizontal,
|
|
6315
|
+
bandPosition,
|
|
6316
|
+
slotOffset,
|
|
6317
|
+
slotThickness: groupScale.bandwidth(),
|
|
6318
|
+
valuePositions: [getValueScaleBaseline(valueScale), ...valuePositions],
|
|
6319
|
+
widthFactor: selected.thickness
|
|
6320
|
+
});
|
|
6321
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectedRing, {
|
|
6322
|
+
chartId,
|
|
6323
|
+
d: `M${rect.x} ${rect.y}h${rect.width}v${rect.height}h${-rect.width}Z`,
|
|
6324
|
+
side: "outside",
|
|
6325
|
+
testId: "bar-chart-selected-ring"
|
|
6326
|
+
});
|
|
6327
|
+
};
|
|
6328
|
+
//#endregion
|
|
6329
|
+
//#region src/charts/bar-chart/private/bar-layer.tsx
|
|
6330
|
+
/**
|
|
6331
|
+
* Draws the bars in front of any comparison shadows, slotted for an overlap layout and grouped by visx otherwise, then the selected bar's ring.
|
|
6332
|
+
*
|
|
6333
|
+
* @param props - Component props.
|
|
6334
|
+
* @param props.chartId - The chart's id, which keeps the ring's clip path unique on a page with several charts.
|
|
6335
|
+
* @param props.overlapSlots - The overlap layout, or undefined for a grouped one.
|
|
6336
|
+
* @param props.selectedBar - The bar or slot the keyboard has selected.
|
|
6337
|
+
* @param props.primaryEntries - The visible primary series, for a grouped layout.
|
|
6338
|
+
* @param props.primaryKeys - Their labels, in group order.
|
|
6339
|
+
* @param props.getBarBackground - Reads a series' bar fill.
|
|
6340
|
+
* @param props.barClassName - Adds classes to a bar.
|
|
6341
|
+
* @param props.groupPadding - The padding between the bars of a category.
|
|
6342
|
+
* @param props.horizontal - Whether the bars run horizontally.
|
|
6343
|
+
* @param props.xAccessor - Reads a datum's x value.
|
|
6344
|
+
* @param props.yAccessor - Reads a datum's y value.
|
|
6345
|
+
* @return The bars.
|
|
6346
|
+
*/
|
|
6347
|
+
const BarLayer = ({ chartId, overlapSlots, selectedBar, primaryEntries, primaryKeys, getBarBackground, barClassName, groupPadding, horizontal, xAccessor, yAccessor }) => {
|
|
6348
|
+
let bars;
|
|
6349
|
+
if (overlapSlots) bars = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", {
|
|
6350
|
+
className: "visx-bar-group",
|
|
6351
|
+
children: overlapSlots.entries.map(({ series, index }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SlottedBarSeries, {
|
|
6352
|
+
dataKey: series.label,
|
|
6353
|
+
data: series.data,
|
|
6354
|
+
xAccessor,
|
|
6355
|
+
yAccessor,
|
|
6356
|
+
colorAccessor: getBarBackground(index),
|
|
6357
|
+
barClassName,
|
|
6358
|
+
slotKeys: overlapSlots.slotKeys,
|
|
6359
|
+
slotKey: overlapSlots.slotOf(series.label),
|
|
6360
|
+
thickness: overlapSlots.thicknessOf(series.label),
|
|
6361
|
+
groupPadding
|
|
6362
|
+
}, series.label))
|
|
6363
|
+
});
|
|
6364
|
+
else if (barClassName) bars = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", {
|
|
6365
|
+
className: "visx-bar-group",
|
|
6366
|
+
children: primaryEntries.map(({ series, index }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClassifiedBarSeries, {
|
|
6367
|
+
dataKey: series.label,
|
|
6368
|
+
data: series.data,
|
|
6369
|
+
xAccessor,
|
|
6370
|
+
yAccessor,
|
|
6371
|
+
colorAccessor: getBarBackground(index),
|
|
6372
|
+
barClassName,
|
|
6373
|
+
primaryKeys,
|
|
6374
|
+
groupPadding
|
|
6375
|
+
}, series.label))
|
|
6376
|
+
});
|
|
6377
|
+
else bars = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.BarGroup, {
|
|
6378
|
+
padding: groupPadding,
|
|
6379
|
+
children: primaryEntries.map(({ series, index }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.BarSeries, {
|
|
6380
|
+
dataKey: series.label,
|
|
6381
|
+
data: series.data,
|
|
6382
|
+
xAccessor,
|
|
6383
|
+
yAccessor,
|
|
6384
|
+
colorAccessor: getBarBackground(index)
|
|
6385
|
+
}, series.label))
|
|
6386
|
+
});
|
|
6387
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [bars, selectedBar && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectedBarRing, {
|
|
6388
|
+
chartId,
|
|
6389
|
+
selected: selectedBar,
|
|
6390
|
+
slotKeys: overlapSlots?.slotKeys ?? primaryKeys,
|
|
6391
|
+
groupPadding,
|
|
6392
|
+
horizontal,
|
|
6393
|
+
xAccessor,
|
|
6394
|
+
yAccessor
|
|
6395
|
+
})] });
|
|
6396
|
+
};
|
|
6397
|
+
//#endregion
|
|
6398
|
+
//#region src/charts/bar-chart/private/slot-tooltip-members.tsx
|
|
6399
|
+
/**
|
|
6400
|
+
* Fills the tooltip with every bar of the keyboard-selected category, and only those.
|
|
6401
|
+
*
|
|
6402
|
+
* The last member shown becomes the tooltip's nearest datum, so the caller lists the keyboard stop last.
|
|
6403
|
+
*
|
|
6404
|
+
* @param props - Component props.
|
|
6405
|
+
* @param props.members - The category's bars, the keyboard stop last.
|
|
6406
|
+
* @param props.index - The selected data point.
|
|
6407
|
+
* @return No visual content.
|
|
6408
|
+
*/
|
|
6409
|
+
function SlotTooltipMembers({ members, index }) {
|
|
6410
|
+
const { showTooltip, updateTooltip } = (0, react.useContext)(_visx_xychart.TooltipContext) ?? {};
|
|
6411
|
+
(0, react.useEffect)(() => {
|
|
6412
|
+
updateTooltip?.((state) => ({
|
|
6413
|
+
...state,
|
|
6414
|
+
tooltipData: void 0
|
|
6415
|
+
}));
|
|
6416
|
+
members.forEach(({ key, datum }) => showTooltip?.({
|
|
6417
|
+
key,
|
|
6418
|
+
datum,
|
|
6419
|
+
index
|
|
6420
|
+
}));
|
|
6421
|
+
}, [members, index]);
|
|
6422
|
+
return null;
|
|
6423
|
+
}
|
|
6424
|
+
//#endregion
|
|
6425
|
+
//#region src/charts/bar-chart/private/overlap-bars.ts
|
|
6426
|
+
const PRIMARY_SLOT = "primary";
|
|
6427
|
+
const COMPARISON_SLOT = "comparison";
|
|
6428
|
+
const OVERLAP_SLOTS = [PRIMARY_SLOT, COMPARISON_SLOT];
|
|
6429
|
+
const SEARCH_STEPS = 16;
|
|
6430
|
+
/**
|
|
6431
|
+
* Places each series of an overlap layout: primaries in one slot and comparisons in the other, with each `group` a layer, back to front in order of first appearance.
|
|
6432
|
+
*
|
|
6433
|
+
* @param series - The series to place.
|
|
6434
|
+
* @return Each series' placement, by index.
|
|
6435
|
+
*/
|
|
6436
|
+
function getOverlapPlacements(series) {
|
|
6437
|
+
const layerByGroup = /* @__PURE__ */ new Map();
|
|
6438
|
+
series.forEach(({ group }) => {
|
|
6439
|
+
if (!layerByGroup.has(group)) layerByGroup.set(group, layerByGroup.size);
|
|
6440
|
+
});
|
|
6441
|
+
const layerCount = layerByGroup.size;
|
|
6442
|
+
return series.map((item) => {
|
|
6443
|
+
const layer = layerByGroup.get(item.group) ?? 0;
|
|
6444
|
+
return {
|
|
6445
|
+
slot: item.options?.type === "comparison" ? COMPARISON_SLOT : PRIMARY_SLOT,
|
|
6446
|
+
layer,
|
|
6447
|
+
thickness: layerCount > 1 ? 1 - layer * .5 / (layerCount - 1) : 1
|
|
6448
|
+
};
|
|
6449
|
+
});
|
|
6450
|
+
}
|
|
6451
|
+
/**
|
|
6452
|
+
* Blends `from` toward `to` and returns the blend nearest the point where `passes` changes, on its passing side.
|
|
6453
|
+
*
|
|
6454
|
+
* @param from - Where the blend starts.
|
|
6455
|
+
* @param to - Where the blend ends.
|
|
6456
|
+
* @param passes - A test that changes at most once along the blend.
|
|
6457
|
+
* @return A hex color.
|
|
6458
|
+
*/
|
|
6459
|
+
function blendToEdge(from, to, passes) {
|
|
6460
|
+
const passesAtStart = passes(from);
|
|
6461
|
+
let low = 0;
|
|
6462
|
+
let high = 1;
|
|
6463
|
+
for (let step = 0; step < SEARCH_STEPS; step++) {
|
|
6464
|
+
const middle = (low + high) / 2;
|
|
6465
|
+
if (passes(mixHexColors(from, to, middle)) === passesAtStart) low = middle;
|
|
6466
|
+
else high = middle;
|
|
6467
|
+
}
|
|
6468
|
+
return mixHexColors(from, to, passesAtStart ? low : high);
|
|
6469
|
+
}
|
|
6470
|
+
const awayFrom = (background) => contrastRatio(background, "#000000") >= contrastRatio(background, "#ffffff") ? "#000000" : "#ffffff";
|
|
6471
|
+
const opposite = (end) => end === "#000000" ? "#ffffff" : "#000000";
|
|
6472
|
+
/**
|
|
6473
|
+
* The tone of a color nearest the background that keeps 3:1 against it.
|
|
6474
|
+
*
|
|
6475
|
+
* @param color - A hex color.
|
|
6476
|
+
* @param background - The hex chart background.
|
|
6477
|
+
* @return A hex color.
|
|
6478
|
+
*/
|
|
6479
|
+
function tintWithContrast(color, background) {
|
|
6480
|
+
const passes = (tone) => contrastRatio(tone, background) >= 3;
|
|
6481
|
+
return blendToEdge(color, passes(color) ? background : awayFrom(background), passes);
|
|
6482
|
+
}
|
|
6483
|
+
/**
|
|
6484
|
+
* The tone of a color nearest the original that keeps 3:1 against the bar it is drawn over, on the far side of that bar from the background when that side can reach 3:1.
|
|
6485
|
+
*
|
|
6486
|
+
* @param color - A hex color.
|
|
6487
|
+
* @param under - The hex color of the bar behind it.
|
|
6488
|
+
* @param background - The hex chart background.
|
|
6489
|
+
* @return A hex color.
|
|
6490
|
+
*/
|
|
6491
|
+
function shadeWithContrast(color, under, background) {
|
|
6492
|
+
const passes = (tone) => contrastRatio(tone, under) >= 3;
|
|
6493
|
+
const start = contrastRatio(color, background) > contrastRatio(under, background) ? color : under;
|
|
6494
|
+
if (passes(start)) return start;
|
|
6495
|
+
const away = awayFrom(background);
|
|
6496
|
+
return blendToEdge(start, passes(away) ? away : opposite(away), passes);
|
|
6497
|
+
}
|
|
6498
|
+
/**
|
|
6499
|
+
* The colors an overlap layout fills its series with: per slot, the back layer tinted to 3:1 against the background and each layer in front shaded to 3:1 against the one behind. A slot whose front-most series has its own `stroke` keeps its colors as given.
|
|
6500
|
+
*
|
|
6501
|
+
* @param series - Every series of the chart.
|
|
6502
|
+
* @param colors - Each series' resolved color.
|
|
6503
|
+
* @param slotColors - The palette color of each slot, primaries first.
|
|
6504
|
+
* @param background - The hex chart background.
|
|
6505
|
+
* @return Each series' fill, by index.
|
|
6506
|
+
*/
|
|
6507
|
+
function getOverlapBarColors(series, colors, slotColors, background) {
|
|
6508
|
+
const placements = getOverlapPlacements(series);
|
|
6509
|
+
const barColors = [...colors];
|
|
6510
|
+
OVERLAP_SLOTS.forEach((slot, slotIndex) => {
|
|
6511
|
+
const members = placements.flatMap((placement, index) => placement.slot === slot ? [{
|
|
6512
|
+
index,
|
|
6513
|
+
layer: placement.layer
|
|
6514
|
+
}] : []);
|
|
6515
|
+
if (!members.length) return;
|
|
6516
|
+
const layers = [...new Set(members.map(({ layer }) => layer))].sort((a, b) => a - b);
|
|
6517
|
+
const back = members.find(({ layer }) => layer === layers[0]);
|
|
6518
|
+
const base = back && series[back.index].options?.stroke ? colors[back.index] : slotColors[slotIndex];
|
|
6519
|
+
if (members.some(({ index, layer }) => layer === layers[layers.length - 1] && !!series[index].options?.stroke) || !isValidHexColor(base) || !isValidHexColor(background)) return;
|
|
6520
|
+
const toneByLayer = /* @__PURE__ */ new Map();
|
|
6521
|
+
let under;
|
|
6522
|
+
layers.forEach((layer) => {
|
|
6523
|
+
const tone = under === void 0 ? tintWithContrast(base, background) : shadeWithContrast(base, under, background);
|
|
6524
|
+
toneByLayer.set(layer, tone);
|
|
6525
|
+
under = tone;
|
|
6526
|
+
});
|
|
6527
|
+
members.forEach(({ index, layer }) => {
|
|
6528
|
+
barColors[index] = toneByLayer.get(layer) ?? colors[index];
|
|
6529
|
+
});
|
|
6530
|
+
});
|
|
6531
|
+
return barColors;
|
|
6532
|
+
}
|
|
6533
|
+
//#endregion
|
|
6534
|
+
//#region src/charts/bar-chart/private/use-overlap-slots.ts
|
|
6535
|
+
/**
|
|
6536
|
+
* Whether two data points sit at the same category: the same label, or the same date.
|
|
6537
|
+
*
|
|
6538
|
+
* @param point - A data point.
|
|
6539
|
+
* @param category - The data point naming the category.
|
|
6540
|
+
* @return Whether `point` is at `category`.
|
|
6541
|
+
*/
|
|
6542
|
+
function isSameCategory(point, category) {
|
|
6543
|
+
return category.label != null ? point.label === category.label : !!category.date && !!point.date && point.date.getTime() === category.date.getTime();
|
|
6544
|
+
}
|
|
6545
|
+
/**
|
|
6546
|
+
* Lays out the visible series of an overlap layout in slots and layers.
|
|
6547
|
+
*
|
|
6548
|
+
* @param enabled - Whether the chart draws an overlap layout.
|
|
6549
|
+
* @param seriesWithVisibility - Every series, with its index and whether it is visible.
|
|
6550
|
+
* @return The layout, or undefined when the chart is not an overlap layout.
|
|
6551
|
+
*/
|
|
6552
|
+
function useOverlapSlots(enabled, seriesWithVisibility) {
|
|
6553
|
+
return (0, react.useMemo)(() => {
|
|
6554
|
+
if (!enabled) return;
|
|
6555
|
+
const visible = seriesWithVisibility.filter(({ isVisible }) => isVisible);
|
|
6556
|
+
const placements = getOverlapPlacements(visible.map(({ series }) => series));
|
|
6557
|
+
const placed = visible.map(({ series, index }, position) => ({
|
|
6558
|
+
entry: {
|
|
6559
|
+
series,
|
|
6560
|
+
index
|
|
6561
|
+
},
|
|
6562
|
+
placement: placements[position]
|
|
6563
|
+
})).sort((a, b) => a.placement.layer - b.placement.layer);
|
|
6564
|
+
const entries = placed.map(({ entry }) => entry);
|
|
6565
|
+
const placementByKey = new Map(placed.map(({ entry, placement }) => [entry.series.label, placement]));
|
|
6566
|
+
const slotOf = (key) => placementByKey.get(key)?.slot ?? "primary";
|
|
6567
|
+
const thicknessOf = (key) => placementByKey.get(key)?.thickness ?? 1;
|
|
6568
|
+
const hasPrimaries = seriesWithVisibility.some(({ series }) => series.options?.type !== "comparison");
|
|
6569
|
+
const showsComparisons = entries.some(({ series }) => slotOf(series.label) === COMPARISON_SLOT);
|
|
6570
|
+
const slotKeys = [...hasPrimaries ? [PRIMARY_SLOT] : [], ...showsComparisons ? [COMPARISON_SLOT] : []];
|
|
6571
|
+
return {
|
|
6572
|
+
entries,
|
|
6573
|
+
keys: entries.map(({ series }) => series.label),
|
|
6574
|
+
slotKeys,
|
|
6575
|
+
slotOf,
|
|
6576
|
+
thicknessOf,
|
|
6577
|
+
positions: getStopPositions(entries, slotKeys, slotOf)
|
|
6578
|
+
};
|
|
6579
|
+
}, [enabled, seriesWithVisibility]);
|
|
6580
|
+
}
|
|
6581
|
+
/**
|
|
6582
|
+
* The category a data point sits at, as a key two points share when `isSameCategory` holds.
|
|
6583
|
+
*
|
|
6584
|
+
* @param point - A data point.
|
|
6585
|
+
* @return The category key.
|
|
6586
|
+
*/
|
|
6587
|
+
function categoryKey(point) {
|
|
6588
|
+
return point.label != null ? `label:${point.label}` : `date:${point.date?.getTime()}`;
|
|
6589
|
+
}
|
|
6590
|
+
/**
|
|
6591
|
+
* Lists a stop for every slot at every category that slot draws a bar at, category by category.
|
|
6592
|
+
*
|
|
6593
|
+
* @param entries - The visible series, back to front.
|
|
6594
|
+
* @param slotKeys - The slots, in band order.
|
|
6595
|
+
* @param slotOf - The slot a series sits in.
|
|
6596
|
+
* @return The positions, in keyboard order.
|
|
6597
|
+
*/
|
|
6598
|
+
function getStopPositions(entries, slotKeys, slotOf) {
|
|
6599
|
+
const categories = /* @__PURE__ */ new Map();
|
|
6600
|
+
entries.forEach(({ series }) => {
|
|
6601
|
+
series.data.forEach((datum, dataPointIndex) => {
|
|
6602
|
+
const key = categoryKey(datum);
|
|
6603
|
+
const slots = categories.get(key) ?? /* @__PURE__ */ new Map();
|
|
6604
|
+
categories.set(key, slots);
|
|
6605
|
+
if (!isBarRendered(datum)) return;
|
|
6606
|
+
const slotKey = slotOf(series.label);
|
|
6607
|
+
slots.set(slotKey, {
|
|
6608
|
+
slotKey,
|
|
6609
|
+
key: series.label,
|
|
6610
|
+
datum,
|
|
6611
|
+
dataPointIndex
|
|
6612
|
+
});
|
|
6613
|
+
});
|
|
6614
|
+
});
|
|
6615
|
+
const ordered = [...categories.values()].filter((slots) => slots.size > 0);
|
|
6616
|
+
if (ordered.every((slots) => [...slots.values()].every(({ datum }) => datum.label == null && datum.date))) {
|
|
6617
|
+
const time = (slots) => [...slots.values()][0].datum.date?.getTime() ?? 0;
|
|
6618
|
+
ordered.sort((a, b) => time(a) - time(b));
|
|
6619
|
+
}
|
|
6620
|
+
return ordered.flatMap((slots) => slotKeys.flatMap((slotKey) => {
|
|
6621
|
+
const position = slots.get(slotKey);
|
|
6622
|
+
return position ? [position] : [];
|
|
6623
|
+
}));
|
|
6624
|
+
}
|
|
6625
|
+
/**
|
|
6626
|
+
* The slot a keyboard index selects, and every bar at its category.
|
|
6627
|
+
*
|
|
6628
|
+
* @param slots - The overlap layout.
|
|
6629
|
+
* @param selectedIndex - The keyboard index into `slots.positions`.
|
|
6630
|
+
* @return The selected slot, or undefined when nothing is selected or drawn there.
|
|
6631
|
+
*/
|
|
6632
|
+
function getSelectedSlot(slots, selectedIndex) {
|
|
6633
|
+
const position = selectedIndex === void 0 ? void 0 : slots?.positions[selectedIndex];
|
|
6634
|
+
if (!slots || !position) return;
|
|
6635
|
+
const { slotKey, key: stopKey, datum: category, dataPointIndex } = position;
|
|
6636
|
+
const members = slots.entries.flatMap(({ series }) => {
|
|
6637
|
+
const datum = series.data.find((point) => isSameCategory(point, category));
|
|
6638
|
+
return datum ? [{
|
|
6639
|
+
key: series.label,
|
|
6640
|
+
datum
|
|
6641
|
+
}] : [];
|
|
6642
|
+
});
|
|
6643
|
+
const slotBars = members.filter(({ key, datum }) => isBarRendered(datum) && slots.slotOf(key) === slotKey);
|
|
6644
|
+
return {
|
|
6645
|
+
data: slotBars.map(({ datum }) => datum),
|
|
6646
|
+
slotKey,
|
|
6647
|
+
thickness: Math.max(...slotBars.map(({ key }) => slots.thicknessOf(key))),
|
|
6648
|
+
index: dataPointIndex,
|
|
6649
|
+
members: [...members.filter(({ key }) => key !== stopKey), ...members.filter(({ key }) => key === stopKey)]
|
|
6650
|
+
};
|
|
6651
|
+
}
|
|
6652
|
+
//#endregion
|
|
6653
|
+
//#region src/charts/bar-chart/private/use-bar-stops.ts
|
|
6654
|
+
/**
|
|
6655
|
+
* Lays out the keyboard stops: one per slot and category in an overlap layout, else one per primary bar, series-major within each category.
|
|
6656
|
+
*
|
|
6657
|
+
* @param overlapSlots - The overlap layout, or undefined for a grouped one.
|
|
6658
|
+
* @param primaryEntries - The visible primary series.
|
|
6659
|
+
* @return The stops.
|
|
6660
|
+
*/
|
|
6661
|
+
function useBarStops(overlapSlots, primaryEntries) {
|
|
6662
|
+
const series = (0, react.useMemo)(() => overlapSlots ? [] : primaryEntries.map((entry) => entry.series), [overlapSlots, primaryEntries]);
|
|
6663
|
+
return {
|
|
6664
|
+
count: overlapSlots?.positions.length ?? Math.max(0, ...series.map((item) => item.data.length)) * series.length,
|
|
6665
|
+
series,
|
|
6666
|
+
key: (0, react.useMemo)(() => JSON.stringify(overlapSlots?.keys ?? series.map((item) => item.label)), [overlapSlots, series]),
|
|
6667
|
+
barAt: (0, react.useCallback)((stop) => {
|
|
6668
|
+
if (overlapSlots) {
|
|
6669
|
+
const position = overlapSlots.positions[stop];
|
|
6670
|
+
return position && {
|
|
6671
|
+
datum: position.datum,
|
|
6672
|
+
index: position.dataPointIndex,
|
|
6673
|
+
key: position.key
|
|
6674
|
+
};
|
|
6675
|
+
}
|
|
6676
|
+
if (!series.length) return;
|
|
6677
|
+
const index = Math.floor(stop / series.length);
|
|
6678
|
+
const item = series[stop % series.length];
|
|
6679
|
+
const datum = item?.data[index];
|
|
6680
|
+
return item && datum ? {
|
|
6681
|
+
datum,
|
|
6682
|
+
index,
|
|
6683
|
+
key: item.label
|
|
6684
|
+
} : void 0;
|
|
6685
|
+
}, [overlapSlots, series]),
|
|
6686
|
+
stopOf: (0, react.useCallback)((seriesKey, dataPointIndex) => {
|
|
6687
|
+
if (overlapSlots) {
|
|
6688
|
+
const hovered = overlapSlots.entries.find((entry) => entry.series.label === seriesKey)?.series.data[dataPointIndex];
|
|
6689
|
+
const slotKey = overlapSlots.slotOf(seriesKey);
|
|
6690
|
+
const stop = hovered ? overlapSlots.positions.findIndex((position) => position.slotKey === slotKey && isSameCategory(position.datum, hovered)) : -1;
|
|
6691
|
+
return stop >= 0 ? stop : void 0;
|
|
6692
|
+
}
|
|
6693
|
+
const seriesIndex = series.findIndex((item) => item.label === seriesKey);
|
|
6694
|
+
return seriesIndex >= 0 ? dataPointIndex * series.length + seriesIndex : void 0;
|
|
6695
|
+
}, [overlapSlots, series])
|
|
6696
|
+
};
|
|
6697
|
+
}
|
|
6181
6698
|
/**
|
|
6182
6699
|
* Upper clamp on the computed group padding, so bars can never collapse to zero width
|
|
6183
6700
|
* even at very large `widthFactor` values.
|
|
@@ -6213,9 +6730,7 @@ const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizont
|
|
|
6213
6730
|
data: series,
|
|
6214
6731
|
index
|
|
6215
6732
|
});
|
|
6216
|
-
const opacity = barStyles?.opacity ?? .5;
|
|
6217
6733
|
const widthFactor = barStyles?.widthFactor ?? 1.5;
|
|
6218
|
-
const fill = resolveFill(entry);
|
|
6219
6734
|
series.data.forEach((datum, i) => {
|
|
6220
6735
|
const bandPosition = Number(bandScale(bandAccessor(datum)));
|
|
6221
6736
|
const valuePosition = Number(valueScale(Number(valueAccessor(datum))));
|
|
@@ -6223,13 +6738,12 @@ const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizont
|
|
|
6223
6738
|
if (process.env.NODE_ENV !== "production" && !Number.isFinite(bandPosition)) console.warn(`[Charts] ComparisonBars: datum key "${String(bandAccessor(datum))}" did not match any primary category. Shadow will not be rendered. Ensure comparison series data uses the same label/date keys as the primary series.`);
|
|
6224
6739
|
return;
|
|
6225
6740
|
}
|
|
6226
|
-
const rect =
|
|
6741
|
+
const rect = computeSlotRect({
|
|
6227
6742
|
horizontal,
|
|
6228
6743
|
bandPosition,
|
|
6229
6744
|
slotOffset,
|
|
6230
6745
|
slotThickness,
|
|
6231
|
-
valuePosition,
|
|
6232
|
-
baseline,
|
|
6746
|
+
valuePositions: [baseline, valuePosition],
|
|
6233
6747
|
widthFactor
|
|
6234
6748
|
});
|
|
6235
6749
|
rects.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
@@ -6238,8 +6752,8 @@ const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizont
|
|
|
6238
6752
|
y: rect.y,
|
|
6239
6753
|
width: rect.width,
|
|
6240
6754
|
height: rect.height,
|
|
6241
|
-
fill,
|
|
6242
|
-
opacity
|
|
6755
|
+
fill: resolveFill(entry, datum),
|
|
6756
|
+
opacity: barStyles?.opacity
|
|
6243
6757
|
}, `${index}-${i}`));
|
|
6244
6758
|
});
|
|
6245
6759
|
});
|
|
@@ -6338,7 +6852,8 @@ function nearestBandIndex(data, accessor, scale, position) {
|
|
|
6338
6852
|
/**
|
|
6339
6853
|
* Correct visx's range-based band inversion, which omits outer padding.
|
|
6340
6854
|
* @param root0 - Registered series selection.
|
|
6341
|
-
* @param root0.keys - Visible series keys.
|
|
6855
|
+
* @param root0.keys - Visible series keys, back to front where bars overlap.
|
|
6856
|
+
* @param root0.slots - Where series share slots: the slots of a category, in the order the band lays them out, and the slot each series sits in.
|
|
6342
6857
|
* @param root0.groupPadding - Padding between grouped bars.
|
|
6343
6858
|
* @param root0.withTooltips - Whether pointer events update the tooltip.
|
|
6344
6859
|
* @param root0.onPointerDown - Receives the corrected pointer-down datum.
|
|
@@ -6346,12 +6861,13 @@ function nearestBandIndex(data, accessor, scale, position) {
|
|
|
6346
6861
|
* @param root0.onPointerMove - Receives the corrected datum under a moving pointer.
|
|
6347
6862
|
* @return No visual content.
|
|
6348
6863
|
*/
|
|
6349
|
-
function BandTooltip({ keys, groupPadding, withTooltips, onPointerDown, onPointerUp, onPointerMove }) {
|
|
6864
|
+
function BandTooltip({ keys, slots, groupPadding, withTooltips, onPointerDown, onPointerUp, onPointerMove }) {
|
|
6350
6865
|
const { xScale, yScale, dataRegistry, horizontal } = (0, react.useContext)(_visx_xychart.DataContext);
|
|
6351
|
-
const { showTooltip } = (0, react.useContext)(_visx_xychart.TooltipContext);
|
|
6866
|
+
const { showTooltip, hideTooltip, updateTooltip } = (0, react.useContext)(_visx_xychart.TooltipContext);
|
|
6352
6867
|
const scale = horizontal ? yScale : xScale;
|
|
6353
6868
|
const bandwidth = scale?.bandwidth?.() ?? 0;
|
|
6354
|
-
const groupScale = (0, react.useMemo)(() => createGroupScale(keys, bandwidth, groupPadding), [
|
|
6869
|
+
const groupScale = (0, react.useMemo)(() => createGroupScale(slots?.keys ?? keys, bandwidth, groupPadding), [
|
|
6870
|
+
slots,
|
|
6355
6871
|
keys,
|
|
6356
6872
|
bandwidth,
|
|
6357
6873
|
groupPadding
|
|
@@ -6359,18 +6875,21 @@ function BandTooltip({ keys, groupPadding, withTooltips, onPointerDown, onPointe
|
|
|
6359
6875
|
const getSelections = (0, react.useCallback)((params) => {
|
|
6360
6876
|
const point = params?.svgPoint;
|
|
6361
6877
|
if (!point || !scale?.bandwidth) return [];
|
|
6878
|
+
const position = horizontal ? point.y : point.x;
|
|
6362
6879
|
const selections = [];
|
|
6880
|
+
const bands = [];
|
|
6363
6881
|
for (const key of keys) {
|
|
6364
6882
|
const entry = dataRegistry?.get(key);
|
|
6365
6883
|
if (!entry) continue;
|
|
6366
6884
|
const accessor = horizontal ? entry.yAccessor : entry.xAccessor;
|
|
6367
|
-
const index = nearestBandIndex(entry.data, accessor, scale,
|
|
6885
|
+
const index = nearestBandIndex(entry.data, accessor, scale, position);
|
|
6368
6886
|
if (index < 0) continue;
|
|
6369
6887
|
const datum = entry.data[index];
|
|
6370
|
-
const
|
|
6888
|
+
const band = Number(scale(accessor(datum)));
|
|
6889
|
+
const start = band + Number(groupScale(slots ? slots.slotOf(key) : key));
|
|
6371
6890
|
const end = start + groupScale.step();
|
|
6372
|
-
const position = horizontal ? point.y : point.x;
|
|
6373
6891
|
const distance = position >= start && position <= end ? 0 : Math.abs(position - (start + end) / 2);
|
|
6892
|
+
bands.push(band);
|
|
6374
6893
|
selections.push({
|
|
6375
6894
|
event: params.event,
|
|
6376
6895
|
key,
|
|
@@ -6381,12 +6900,16 @@ function BandTooltip({ keys, groupPadding, withTooltips, onPointerDown, onPointe
|
|
|
6381
6900
|
distanceY: horizontal ? distance : 0
|
|
6382
6901
|
});
|
|
6383
6902
|
}
|
|
6384
|
-
return selections;
|
|
6903
|
+
if (!slots) return selections;
|
|
6904
|
+
const bandDistance = (band) => Math.abs(band + scale.bandwidth() / 2 - position);
|
|
6905
|
+
const hoveredBand = bands.reduce((best, band) => best === void 0 || bandDistance(band) < bandDistance(best) ? band : best, void 0);
|
|
6906
|
+
return selections.filter((_, index) => bands[index] === hoveredBand);
|
|
6385
6907
|
}, [
|
|
6386
6908
|
scale,
|
|
6387
6909
|
dataRegistry,
|
|
6388
6910
|
horizontal,
|
|
6389
6911
|
keys,
|
|
6912
|
+
slots,
|
|
6390
6913
|
groupScale
|
|
6391
6914
|
]);
|
|
6392
6915
|
const handlePointer = (0, react.useCallback)((params) => {
|
|
@@ -6396,24 +6919,162 @@ function BandTooltip({ keys, groupPadding, withTooltips, onPointerDown, onPointe
|
|
|
6396
6919
|
return !best || distance(selection) <= distance(best) ? selection : best;
|
|
6397
6920
|
}, void 0);
|
|
6398
6921
|
if (params?.event.type === "pointermove" && nearest) onPointerMove?.(nearest);
|
|
6399
|
-
if (withTooltips && params?.event.type !== "pointerup")
|
|
6922
|
+
if (withTooltips && params?.event.type !== "pointerup") {
|
|
6923
|
+
if (slots) updateTooltip((state) => ({
|
|
6924
|
+
...state,
|
|
6925
|
+
tooltipData: void 0
|
|
6926
|
+
}));
|
|
6927
|
+
selections.forEach(showTooltip);
|
|
6928
|
+
}
|
|
6400
6929
|
const callback = params?.event.type === "pointerdown" ? onPointerDown : onPointerUp;
|
|
6401
6930
|
if (params?.event.type === "pointermove" || !callback) return;
|
|
6402
6931
|
if (nearest) callback(nearest);
|
|
6403
6932
|
}, [
|
|
6404
6933
|
getSelections,
|
|
6405
6934
|
withTooltips,
|
|
6935
|
+
slots,
|
|
6406
6936
|
showTooltip,
|
|
6937
|
+
updateTooltip,
|
|
6407
6938
|
onPointerDown,
|
|
6408
6939
|
onPointerUp,
|
|
6409
6940
|
onPointerMove
|
|
6410
6941
|
]);
|
|
6411
6942
|
(0, _visx_xychart.useEventEmitter)("pointermove", withTooltips ? handlePointer : void 0);
|
|
6943
|
+
(0, _visx_xychart.useEventEmitter)("pointerout", withTooltips && slots ? hideTooltip : void 0);
|
|
6412
6944
|
(0, _visx_xychart.useEventEmitter)("pointerdown", handlePointer);
|
|
6413
6945
|
(0, _visx_xychart.useEventEmitter)("pointerup", onPointerUp ? handlePointer : void 0);
|
|
6414
6946
|
return null;
|
|
6415
6947
|
}
|
|
6416
6948
|
//#endregion
|
|
6949
|
+
//#region src/charts/bar-chart/private/grouped-bars.ts
|
|
6950
|
+
/**
|
|
6951
|
+
* Pairs each comparison series with the primary of its `group`, or with the only primary.
|
|
6952
|
+
*
|
|
6953
|
+
* @param series - The chart's series.
|
|
6954
|
+
* @return The index of each paired comparison's primary, keyed by the comparison's index.
|
|
6955
|
+
*/
|
|
6956
|
+
function getComparisonPrimaries(series) {
|
|
6957
|
+
const primaries = series.flatMap((item, index) => item.options?.type === "comparison" ? [] : [{
|
|
6958
|
+
group: item.group,
|
|
6959
|
+
index
|
|
6960
|
+
}]);
|
|
6961
|
+
const primaryByGroup = new Map(primaries.map(({ group, index }) => [group, index]));
|
|
6962
|
+
const onlyPrimary = primaries.length === 1 ? primaries[0].index : void 0;
|
|
6963
|
+
const pairs = /* @__PURE__ */ new Map();
|
|
6964
|
+
series.forEach((item, index) => {
|
|
6965
|
+
const primary = item.options?.type === "comparison" ? primaryByGroup.get(item.group) ?? onlyPrimary : void 0;
|
|
6966
|
+
if (primary !== void 0) pairs.set(index, primary);
|
|
6967
|
+
});
|
|
6968
|
+
return pairs;
|
|
6969
|
+
}
|
|
6970
|
+
/**
|
|
6971
|
+
* The colors a grouped bar chart fills its series with. A comparison takes the lightest tint of its primary's color that keeps 3:1 against the background, and the primary in front of it the shade that keeps 3:1 against that tint. A primary with its own `stroke` keeps it, and a comparison with its own, different `stroke` keeps the pair's colors as given.
|
|
6972
|
+
*
|
|
6973
|
+
* @param series - The chart's series.
|
|
6974
|
+
* @param colors - Each series' resolved color.
|
|
6975
|
+
* @param background - The hex chart background.
|
|
6976
|
+
* @return Each series' fill, by index.
|
|
6977
|
+
*/
|
|
6978
|
+
function getGroupedBarColors(series, colors, background) {
|
|
6979
|
+
const barColors = [...colors];
|
|
6980
|
+
getComparisonPrimaries(series).forEach((primary, comparison) => {
|
|
6981
|
+
const base = colors[primary];
|
|
6982
|
+
if (!!series[comparison].options?.stroke && colors[comparison].toLowerCase() !== base.toLowerCase() || !isValidHexColor(base) || !isValidHexColor(background)) return;
|
|
6983
|
+
const tint = tintWithContrast(base, background);
|
|
6984
|
+
barColors[comparison] = tint;
|
|
6985
|
+
if (!series[primary].options?.stroke) barColors[primary] = shadeWithContrast(base, tint, background);
|
|
6986
|
+
});
|
|
6987
|
+
return barColors;
|
|
6988
|
+
}
|
|
6989
|
+
//#endregion
|
|
6990
|
+
//#region src/charts/bar-chart/use-bar-colors.ts
|
|
6991
|
+
/**
|
|
6992
|
+
* Reads the colors `BarChart` fills series with, so a custom tooltip or legend can match the bars.
|
|
6993
|
+
*
|
|
6994
|
+
* @return A function from series and a layout to each series' bar color, by index.
|
|
6995
|
+
*/
|
|
6996
|
+
function useBarColors() {
|
|
6997
|
+
const { getElementStyles, backgroundColor = BACKGROUND_FALLBACK, theme } = useGlobalChartsContext();
|
|
6998
|
+
const comparisonOpacity = theme.barChart?.barStyles?.comparison?.opacity;
|
|
6999
|
+
return (0, react.useCallback)((series, { layout = "grouped" } = {}) => {
|
|
7000
|
+
const colors = series.map((data, index) => getElementStyles({
|
|
7001
|
+
data,
|
|
7002
|
+
index
|
|
7003
|
+
}).color);
|
|
7004
|
+
if (layout !== "overlap") return comparisonOpacity === void 0 ? getGroupedBarColors(series, colors, backgroundColor) : colors;
|
|
7005
|
+
return getOverlapBarColors(series, colors, [getElementStyles({ index: 0 }).color, getElementStyles({ index: 1 }).color], backgroundColor);
|
|
7006
|
+
}, [
|
|
7007
|
+
getElementStyles,
|
|
7008
|
+
backgroundColor,
|
|
7009
|
+
comparisonOpacity
|
|
7010
|
+
]);
|
|
7011
|
+
}
|
|
7012
|
+
//#endregion
|
|
7013
|
+
//#region src/charts/bar-chart/use-bar-legend-items.ts
|
|
7014
|
+
const isComparison = (data, label) => data.some((series) => series.label === label && series.options?.type === "comparison");
|
|
7015
|
+
/**
|
|
7016
|
+
* Applies what a bar layout changes about a legend: overlap lists every primary before every comparison, as it draws their slots, with every swatch opaque like its bar; a collapsed grouped item takes its comparison's tint, the wider bar that frames the group.
|
|
7017
|
+
*
|
|
7018
|
+
* @param data - The series the items were built from.
|
|
7019
|
+
* @param items - The legend items.
|
|
7020
|
+
* @param layout - The layout the bars are drawn in.
|
|
7021
|
+
* @param seriesColors - Each series' bar color, by index.
|
|
7022
|
+
* @return The legend items.
|
|
7023
|
+
*/
|
|
7024
|
+
function forBarLayout(data, items, layout, seriesColors) {
|
|
7025
|
+
if (layout === "overlap") {
|
|
7026
|
+
const opaque = items.map((item) => item.shapeStyle ? {
|
|
7027
|
+
...item,
|
|
7028
|
+
shapeStyle: {
|
|
7029
|
+
...item.shapeStyle,
|
|
7030
|
+
opacity: void 0
|
|
7031
|
+
}
|
|
7032
|
+
} : item);
|
|
7033
|
+
const seriesItems = opaque.filter((item) => item.seriesLabels);
|
|
7034
|
+
const firstLabel = (item) => item.seriesLabels?.[0];
|
|
7035
|
+
return [
|
|
7036
|
+
...seriesItems.filter((item) => !isComparison(data, firstLabel(item))),
|
|
7037
|
+
...seriesItems.filter((item) => isComparison(data, firstLabel(item))),
|
|
7038
|
+
...opaque.filter((item) => !item.seriesLabels)
|
|
7039
|
+
];
|
|
7040
|
+
}
|
|
7041
|
+
return items.map((item) => {
|
|
7042
|
+
if ((item.seriesLabels?.length ?? 0) < 2) return item;
|
|
7043
|
+
const comparisonIndex = data.findIndex((series) => series.options?.type === "comparison" && item.seriesLabels?.includes(series.label));
|
|
7044
|
+
return comparisonIndex === -1 ? item : {
|
|
7045
|
+
...item,
|
|
7046
|
+
color: seriesColors[comparisonIndex]
|
|
7047
|
+
};
|
|
7048
|
+
});
|
|
7049
|
+
}
|
|
7050
|
+
/**
|
|
7051
|
+
* Builds legend items that match `BarChart`'s bars in a layout, for a custom legend or a chart's own.
|
|
7052
|
+
*
|
|
7053
|
+
* @param data - The series the chart draws.
|
|
7054
|
+
* @param options - The legend options, and the layout the bars are drawn in.
|
|
7055
|
+
* @param legendShape - The shape of the legend swatches.
|
|
7056
|
+
* @return The legend items.
|
|
7057
|
+
*/
|
|
7058
|
+
function useBarLegendItems(data, options = {}, legendShape) {
|
|
7059
|
+
const { layout } = options;
|
|
7060
|
+
const getBarColors = useBarColors();
|
|
7061
|
+
const seriesColors = (0, react.useMemo)(() => layout ? getBarColors(data, { layout }) : void 0, [
|
|
7062
|
+
data,
|
|
7063
|
+
layout,
|
|
7064
|
+
getBarColors
|
|
7065
|
+
]);
|
|
7066
|
+
const items = useChartLegendItems(data, (0, react.useMemo)(() => ({
|
|
7067
|
+
...options,
|
|
7068
|
+
seriesColors
|
|
7069
|
+
}), [options, seriesColors]), legendShape);
|
|
7070
|
+
return (0, react.useMemo)(() => layout && seriesColors ? forBarLayout(data, items, layout, seriesColors) : items, [
|
|
7071
|
+
data,
|
|
7072
|
+
items,
|
|
7073
|
+
layout,
|
|
7074
|
+
seriesColors
|
|
7075
|
+
]);
|
|
7076
|
+
}
|
|
7077
|
+
//#endregion
|
|
6417
7078
|
//#region src/charts/bar-chart/bar-chart.tsx
|
|
6418
7079
|
const validateData$2 = (data) => {
|
|
6419
7080
|
if (!data?.length) return (0, _wordpress_i18n.__)("No data available", "jetpack-charts");
|
|
@@ -6425,13 +7086,14 @@ const renderTooltipRow = (label, value) => /* @__PURE__ */ (0, react_jsx_runtime
|
|
|
6425
7086
|
className: bar_chart_module_default["bar-chart__tooltip-row"],
|
|
6426
7087
|
children: (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n.__)("%1$s: %2$s", "jetpack-charts"), label, value)
|
|
6427
7088
|
});
|
|
6428
|
-
const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, height, className, margin, withTooltips = false, showLegend = false, legend = {}, gridVisibility: gridVisibilityProp, renderTooltip, tooltipPlacement, tooltipAnchorTop, tooltipStyle, barClassName, options = {}, orientation = "vertical", withPatterns = false, showZeroValues = false, withBandHighlight = false, onBandHighlightChange, defaultHiddenSeries, animation, children, gap = "md", onPointerDown, onPointerUp, onDatumActivate }) => {
|
|
7089
|
+
const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, height, className, margin, withTooltips = false, showLegend = false, legend = {}, gridVisibility: gridVisibilityProp, renderTooltip, tooltipPlacement, tooltipAnchorTop, tooltipStyle, barClassName, options = {}, orientation = "vertical", layout = "grouped", withPatterns = false, showZeroValues = false, withBandHighlight = false, onBandHighlightChange, defaultHiddenSeries, animation, children, gap = "md", onPointerDown, onPointerUp, onDatumActivate }) => {
|
|
6429
7090
|
if (!withTooltips && (withBandHighlight || onBandHighlightChange)) warnOnce("bar-chart-band-highlight-without-tooltips", "BarChart: withBandHighlight and onBandHighlightChange require withTooltips.");
|
|
6430
7091
|
const legendInteractive = legend.interactive ?? false;
|
|
6431
7092
|
const legendCollapseGroups = legend.collapseGroups ?? false;
|
|
6432
7093
|
const legendComparisonItem = legend.comparisonItem ?? false;
|
|
6433
7094
|
const legendComparisonSeriesItems = legend.comparisonSeriesItems;
|
|
6434
7095
|
const horizontal = orientation === "horizontal";
|
|
7096
|
+
const isOverlap = layout === "overlap";
|
|
6435
7097
|
const chartId = useChartId(providedChartId);
|
|
6436
7098
|
const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
|
|
6437
7099
|
const isSeriesVisible = (0, react.useCallback)((seriesLabel) => !hiddenSeries.has(seriesLabel), [hiddenSeries]);
|
|
@@ -6441,16 +7103,25 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
|
|
|
6441
7103
|
enabled: showZeroValues,
|
|
6442
7104
|
valueAxisLength: horizontal ? width : height
|
|
6443
7105
|
});
|
|
6444
|
-
const legendItems =
|
|
7106
|
+
const legendItems = useBarLegendItems(dataSorted, (0, react.useMemo)(() => ({
|
|
6445
7107
|
collapseGroups: legendCollapseGroups,
|
|
6446
7108
|
comparisonItem: legendComparisonItem,
|
|
6447
|
-
comparisonSeriesItems: legendComparisonSeriesItems
|
|
7109
|
+
comparisonSeriesItems: legendComparisonSeriesItems,
|
|
7110
|
+
layout
|
|
6448
7111
|
}), [
|
|
6449
7112
|
legendCollapseGroups,
|
|
6450
7113
|
legendComparisonItem,
|
|
6451
|
-
legendComparisonSeriesItems
|
|
7114
|
+
legendComparisonSeriesItems,
|
|
7115
|
+
layout
|
|
6452
7116
|
]));
|
|
6453
|
-
const { getElementStyles } = useGlobalChartsContext();
|
|
7117
|
+
const { getElementStyles, theme: providerTheme } = useGlobalChartsContext();
|
|
7118
|
+
const getBarColors = useBarColors();
|
|
7119
|
+
const barColors = (0, react.useMemo)(() => getBarColors(dataSorted, { layout }), [
|
|
7120
|
+
layout,
|
|
7121
|
+
getBarColors,
|
|
7122
|
+
dataSorted
|
|
7123
|
+
]);
|
|
7124
|
+
if (providerTheme.barChart?.barStyles?.comparison?.opacity !== void 0) warnOnce("bar-chart-comparison-opacity", "BarChart: theme.barChart.barStyles.comparison.opacity is deprecated. Comparison bars are opaque; leave it unset.");
|
|
6454
7125
|
const isSeriesRendered = (0, react.useCallback)((series) => isSeriesVisible(series.label), [isSeriesVisible]);
|
|
6455
7126
|
const hasWholeNumberValues = (0, react.useMemo)(() => hasOnlyWholeNumbers(dataSorted.filter(isSeriesRendered)), [dataSorted, isSeriesRendered]);
|
|
6456
7127
|
const chartOptions = useBarChartOptions(dataWithVisibleZeros, horizontal, options, isSeriesRendered);
|
|
@@ -6479,37 +7150,30 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
|
|
|
6479
7150
|
return seriesWithVisibility.every(({ isVisible }) => !isVisible);
|
|
6480
7151
|
}, [seriesWithVisibility]);
|
|
6481
7152
|
const primaryEntries = (0, react.useMemo)(() => seriesWithVisibility.filter(({ isVisible, series }) => isVisible && series.options?.type !== "comparison"), [seriesWithVisibility]);
|
|
6482
|
-
const
|
|
7153
|
+
const overlapSlots = useOverlapSlots(isOverlap, seriesWithVisibility);
|
|
6483
7154
|
const primaryKeys = (0, react.useMemo)(() => primaryEntries.map(({ series }) => series.label), [primaryEntries]);
|
|
6484
|
-
const
|
|
6485
|
-
const
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
const datum = series?.data[dataPointIndex];
|
|
6491
|
-
if (series && datum) onDatumActivate?.({
|
|
6492
|
-
datum,
|
|
6493
|
-
index: dataPointIndex,
|
|
6494
|
-
key: series.label
|
|
6495
|
-
});
|
|
6496
|
-
}, [primaryEntries, onDatumActivate]);
|
|
6497
|
-
const visibleSeriesKey = (0, react.useMemo)(() => JSON.stringify(primaryKeys), [primaryKeys]);
|
|
7155
|
+
const stops = useBarStops(overlapSlots, primaryEntries);
|
|
7156
|
+
const { barAt, stopOf } = stops;
|
|
7157
|
+
const activateSelectedBar = (0, react.useCallback)((stop) => {
|
|
7158
|
+
const bar = barAt(stop);
|
|
7159
|
+
if (bar) onDatumActivate?.(bar);
|
|
7160
|
+
}, [barAt, onDatumActivate]);
|
|
6498
7161
|
const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown, onChartPointerMove } = useKeyboardNavigation({
|
|
6499
7162
|
selectedIndex,
|
|
6500
7163
|
setSelectedIndex,
|
|
6501
7164
|
isNavigating,
|
|
6502
7165
|
setIsNavigating,
|
|
6503
7166
|
chartRef,
|
|
6504
|
-
totalPoints,
|
|
7167
|
+
totalPoints: stops.count,
|
|
6505
7168
|
onActivate: activateSelectedBar,
|
|
6506
|
-
visibleSeriesKey
|
|
7169
|
+
visibleSeriesKey: stops.key
|
|
6507
7170
|
});
|
|
6508
7171
|
const handlePointerMove = (0, react.useCallback)(({ key, index }) => {
|
|
6509
|
-
const
|
|
6510
|
-
if (
|
|
6511
|
-
}, [
|
|
7172
|
+
const stop = stopOf(key, index);
|
|
7173
|
+
if (stop !== void 0) onChartPointerMove(stop);
|
|
7174
|
+
}, [stopOf, onChartPointerMove]);
|
|
6512
7175
|
const comparisonEntries = (0, react.useMemo)(() => {
|
|
7176
|
+
if (isOverlap) return [];
|
|
6513
7177
|
const primaryByGroup = new Map(primaryEntries.map(({ series, index }) => [series.group, {
|
|
6514
7178
|
label: series.label,
|
|
6515
7179
|
index
|
|
@@ -6525,12 +7189,12 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
|
|
|
6525
7189
|
entries.push({
|
|
6526
7190
|
series,
|
|
6527
7191
|
index,
|
|
6528
|
-
primaryKey: primary.label
|
|
6529
|
-
primaryIndex: primary.index
|
|
7192
|
+
primaryKey: primary.label
|
|
6530
7193
|
});
|
|
6531
7194
|
});
|
|
6532
7195
|
return entries;
|
|
6533
7196
|
}, [
|
|
7197
|
+
isOverlap,
|
|
6534
7198
|
seriesWithVisibility,
|
|
6535
7199
|
primaryEntries,
|
|
6536
7200
|
primaryKeys
|
|
@@ -6570,23 +7234,32 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
|
|
|
6570
7234
|
chartOptions.yScale,
|
|
6571
7235
|
horizontal
|
|
6572
7236
|
]);
|
|
6573
|
-
const getBarBackground = (0, react.useCallback)((index) => (datum) => withPatterns ? `url(#${getPatternId(chartId, index)})` : datum.color ??
|
|
6574
|
-
data: dataSorted[index],
|
|
6575
|
-
index
|
|
6576
|
-
}).color, [
|
|
7237
|
+
const getBarBackground = (0, react.useCallback)((index) => (datum) => withPatterns ? `url(#${getPatternId(chartId, index)})` : datum.color ?? barColors[index] ?? "", [
|
|
6577
7238
|
withPatterns,
|
|
6578
|
-
|
|
6579
|
-
dataSorted,
|
|
7239
|
+
barColors,
|
|
6580
7240
|
chartId
|
|
6581
7241
|
]);
|
|
6582
|
-
const resolveComparisonFill = (0, react.useCallback)((entry) => withPatterns ? `url(#${getPatternId(chartId, entry.
|
|
6583
|
-
data: entry.series,
|
|
6584
|
-
index: entry.index
|
|
6585
|
-
}).color, [
|
|
7242
|
+
const resolveComparisonFill = (0, react.useCallback)((entry, datum) => withPatterns ? `url(#${getPatternId(chartId, entry.index)})` : datum.color ?? barColors[entry.index] ?? "", [
|
|
6586
7243
|
withPatterns,
|
|
6587
7244
|
chartId,
|
|
6588
|
-
|
|
7245
|
+
barColors
|
|
6589
7246
|
]);
|
|
7247
|
+
const renderOverlapTooltip = (0, react.useCallback)(({ tooltipData }) => {
|
|
7248
|
+
const nearestDatum = tooltipData?.nearestDatum?.datum;
|
|
7249
|
+
if (!nearestDatum) return null;
|
|
7250
|
+
const datumByKey = tooltipData?.datumByKey ?? {};
|
|
7251
|
+
const categoryLabel = chartOptions.tooltip.labelFormatter(nearestDatum.label || (nearestDatum.date ? nearestDatum.date.getTime() : 0), 0, []);
|
|
7252
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7253
|
+
className: bar_chart_module_default["bar-chart__tooltip"],
|
|
7254
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7255
|
+
className: bar_chart_module_default["bar-chart__tooltip-header"],
|
|
7256
|
+
children: categoryLabel
|
|
7257
|
+
}), dataSorted.flatMap(({ label }) => {
|
|
7258
|
+
const datum = datumByKey[label]?.datum;
|
|
7259
|
+
return datum ? [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderTooltipRow(label, formatReading(datum.value)) }, label)] : [];
|
|
7260
|
+
})]
|
|
7261
|
+
});
|
|
7262
|
+
}, [chartOptions.tooltip, dataSorted]);
|
|
6590
7263
|
const renderDefaultTooltip = (0, react.useCallback)(({ tooltipData }) => {
|
|
6591
7264
|
const nearestDatum = tooltipData?.nearestDatum?.datum;
|
|
6592
7265
|
if (!nearestDatum) return null;
|
|
@@ -6661,26 +7334,24 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
|
|
|
6661
7334
|
}
|
|
6662
7335
|
`;
|
|
6663
7336
|
}, [chartId]);
|
|
6664
|
-
const
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
const
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
stroke-width: 2px;
|
|
6678
|
-
}
|
|
6679
|
-
`;
|
|
7337
|
+
const overlapBandSlots = (0, react.useMemo)(() => overlapSlots && {
|
|
7338
|
+
keys: overlapSlots.slotKeys,
|
|
7339
|
+
slotOf: overlapSlots.slotOf
|
|
7340
|
+
}, [overlapSlots]);
|
|
7341
|
+
const selectedSlot = (0, react.useMemo)(() => getSelectedSlot(overlapSlots, selectedIndex), [overlapSlots, selectedIndex]);
|
|
7342
|
+
const selectedBar = (0, react.useMemo)(() => {
|
|
7343
|
+
if (overlapSlots) return selectedSlot;
|
|
7344
|
+
const bar = selectedIndex === void 0 ? void 0 : barAt(selectedIndex);
|
|
7345
|
+
return bar && isBarRendered(bar.datum) ? {
|
|
7346
|
+
data: [bar.datum],
|
|
7347
|
+
slotKey: bar.key,
|
|
7348
|
+
thickness: 1
|
|
7349
|
+
} : void 0;
|
|
6680
7350
|
}, [
|
|
7351
|
+
overlapSlots,
|
|
7352
|
+
selectedSlot,
|
|
6681
7353
|
selectedIndex,
|
|
6682
|
-
|
|
6683
|
-
chartId
|
|
7354
|
+
barAt
|
|
6684
7355
|
]);
|
|
6685
7356
|
const error = validateData$2(dataSorted);
|
|
6686
7357
|
const isDataValid = !error;
|
|
@@ -6701,7 +7372,6 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
|
|
|
6701
7372
|
children: error
|
|
6702
7373
|
});
|
|
6703
7374
|
const gridVisibility = gridVisibilityProp ?? chartOptions.gridVisibility;
|
|
6704
|
-
const highlightedBarStyle = createKeyboardHighlightStyle();
|
|
6705
7375
|
const legendPosition = legend.position ?? "bottom";
|
|
6706
7376
|
const legendElement = showLegend && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Legend, {
|
|
6707
7377
|
orientation: legend.orientation ?? "horizontal",
|
|
@@ -6781,14 +7451,7 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
|
|
|
6781
7451
|
tickValues: (horizontal ? void 0 : valueTicks) ?? chartOptions.axis.y.tickValues
|
|
6782
7452
|
})] })
|
|
6783
7453
|
}),
|
|
6784
|
-
withPatterns && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("defs", { children:
|
|
6785
|
-
data: seriesData,
|
|
6786
|
-
index
|
|
6787
|
-
}).color)) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: dataSorted.map((seriesData, index) => createPatternBorderStyle(index, getElementStyles({
|
|
6788
|
-
data: seriesData,
|
|
6789
|
-
index
|
|
6790
|
-
}).color)) })] }),
|
|
6791
|
-
highlightedBarStyle && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: highlightedBarStyle }),
|
|
7454
|
+
withPatterns && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("defs", { children: barColors.map((color, index) => renderPattern(index, color)) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: barColors.map((color, index) => createPatternBorderStyle(index, color)) })] }),
|
|
6792
7455
|
allSeriesHidden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SvgEmptyState, {
|
|
6793
7456
|
x: width / 2,
|
|
6794
7457
|
y: chartHeight / 2,
|
|
@@ -6806,30 +7469,22 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
|
|
|
6806
7469
|
getElementStyles,
|
|
6807
7470
|
resolveFill: resolveComparisonFill
|
|
6808
7471
|
}),
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.BarGroup, {
|
|
6822
|
-
padding: groupPadding,
|
|
6823
|
-
children: primaryEntries.map(({ series: seriesData, index }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.BarSeries, {
|
|
6824
|
-
dataKey: seriesData?.label,
|
|
6825
|
-
data: seriesData.data,
|
|
6826
|
-
yAccessor: chartOptions.accessors.yAccessor,
|
|
6827
|
-
xAccessor: chartOptions.accessors.xAccessor,
|
|
6828
|
-
colorAccessor: getBarBackground(index)
|
|
6829
|
-
}, seriesData?.label))
|
|
7472
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BarLayer, {
|
|
7473
|
+
chartId,
|
|
7474
|
+
overlapSlots,
|
|
7475
|
+
selectedBar,
|
|
7476
|
+
primaryEntries,
|
|
7477
|
+
primaryKeys,
|
|
7478
|
+
getBarBackground,
|
|
7479
|
+
barClassName,
|
|
7480
|
+
groupPadding,
|
|
7481
|
+
horizontal,
|
|
7482
|
+
xAccessor: chartOptions.accessors.xAccessor,
|
|
7483
|
+
yAccessor: chartOptions.accessors.yAccessor
|
|
6830
7484
|
}),
|
|
6831
7485
|
(withTooltips || onPointerDown || onPointerUp) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BandTooltip, {
|
|
6832
|
-
keys: primaryKeys,
|
|
7486
|
+
keys: overlapSlots?.keys ?? primaryKeys,
|
|
7487
|
+
slots: overlapBandSlots,
|
|
6833
7488
|
groupPadding,
|
|
6834
7489
|
withTooltips,
|
|
6835
7490
|
onPointerDown,
|
|
@@ -6846,6 +7501,10 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
|
|
|
6846
7501
|
...!horizontal && valueTicks ? { tickValues: valueTicks } : {}
|
|
6847
7502
|
})] })
|
|
6848
7503
|
}),
|
|
7504
|
+
withTooltips && selectedSlot && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SlotTooltipMembers, {
|
|
7505
|
+
members: selectedSlot.members,
|
|
7506
|
+
index: selectedSlot.index
|
|
7507
|
+
}),
|
|
6849
7508
|
withTooltips && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(XYChartTooltip, {
|
|
6850
7509
|
tooltipPlacement,
|
|
6851
7510
|
tooltipAnchorTop,
|
|
@@ -6853,11 +7512,11 @@ const BarChartInternal = ({ data, ariaLabel, chartId: providedChartId, width, he
|
|
|
6853
7512
|
detectBounds: true,
|
|
6854
7513
|
snapTooltipToDatumX: true,
|
|
6855
7514
|
snapTooltipToDatumY: true,
|
|
6856
|
-
renderTooltip: renderTooltip || renderDefaultTooltip,
|
|
7515
|
+
renderTooltip: renderTooltip || (isOverlap ? renderOverlapTooltip : renderDefaultTooltip),
|
|
6857
7516
|
selectedIndex,
|
|
6858
7517
|
tooltipRef,
|
|
6859
7518
|
keyboardFocusedClassName: bar_chart_module_default["bar-chart__tooltip--keyboard-focused"],
|
|
6860
|
-
series:
|
|
7519
|
+
series: stops.series,
|
|
6861
7520
|
mode: "individual"
|
|
6862
7521
|
})
|
|
6863
7522
|
]
|
|
@@ -9517,7 +10176,7 @@ function warning(message) {
|
|
|
9517
10176
|
logged.add(message);
|
|
9518
10177
|
}
|
|
9519
10178
|
//#endregion
|
|
9520
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
10179
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/utils/hooks/use-update-effect.mjs
|
|
9521
10180
|
function useUpdateEffect(effect, deps) {
|
|
9522
10181
|
const mountedRef = (0, react.useRef)(false);
|
|
9523
10182
|
(0, react.useEffect)(() => {
|
|
@@ -10612,7 +11271,7 @@ function serializeStyles(args, registered, mergedProps) {
|
|
|
10612
11271
|
}
|
|
10613
11272
|
react["useInsertionEffect"] && react["useInsertionEffect"] || react.useLayoutEffect;
|
|
10614
11273
|
//#endregion
|
|
10615
|
-
//#region ../../../node_modules/.pnpm/@emotion+react@11.14.0_@types+react@
|
|
11274
|
+
//#region ../../../node_modules/.pnpm/@emotion+react@11.14.0_@types+react@19.3.0_react@19.3.0/node_modules/@emotion/react/dist/emotion-element-d59e098f.esm.js
|
|
10616
11275
|
var isBrowser = typeof document !== "undefined";
|
|
10617
11276
|
var EmotionCacheContext = /* #__PURE__ */ react.createContext(typeof HTMLElement !== "undefined" ? /* #__PURE__ */ createCache({ key: "css" }) : null);
|
|
10618
11277
|
EmotionCacheContext.Provider;
|
|
@@ -10721,7 +11380,7 @@ _createEmotion.css;
|
|
|
10721
11380
|
_createEmotion.sheet;
|
|
10722
11381
|
_createEmotion.cache;
|
|
10723
11382
|
//#endregion
|
|
10724
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
11383
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/utils/hooks/use-cx.mjs
|
|
10725
11384
|
var isSerializedStyles = (o) => typeof o !== "undefined" && o !== null && ["name", "styles"].every((p) => typeof o[p] !== "undefined");
|
|
10726
11385
|
var useCx = () => {
|
|
10727
11386
|
const cache = __unsafe_useEmotionCache();
|
|
@@ -10828,7 +11487,7 @@ function memize(fn, options) {
|
|
|
10828
11487
|
return memoized;
|
|
10829
11488
|
}
|
|
10830
11489
|
//#endregion
|
|
10831
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
11490
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/context-system-provider.mjs
|
|
10832
11491
|
var import_es6 = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
10833
11492
|
module.exports = function equal(a, b) {
|
|
10834
11493
|
if (a === b) return true;
|
|
@@ -10896,19 +11555,19 @@ var BaseContextSystemProvider = ({ children, value }) => {
|
|
|
10896
11555
|
};
|
|
10897
11556
|
(0, react.memo)(BaseContextSystemProvider);
|
|
10898
11557
|
//#endregion
|
|
10899
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
11558
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/constants.mjs
|
|
10900
11559
|
var COMPONENT_NAMESPACE = "data-wp-component";
|
|
10901
11560
|
var CONNECTED_NAMESPACE = "data-wp-c16t";
|
|
10902
11561
|
var CONNECT_STATIC_NAMESPACE = "__contextSystemKey__";
|
|
10903
11562
|
//#endregion
|
|
10904
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
11563
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/get-styled-class-name-from-key.mjs
|
|
10905
11564
|
var import_dist = require_dist();
|
|
10906
11565
|
function getStyledClassName(namespace) {
|
|
10907
11566
|
return `components-${(0, import_dist.paramCase)(namespace)}`;
|
|
10908
11567
|
}
|
|
10909
11568
|
var getStyledClassNameFromKey = memize(getStyledClassName);
|
|
10910
11569
|
//#endregion
|
|
10911
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
11570
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/context-connect.mjs
|
|
10912
11571
|
function contextConnect(Component, namespace) {
|
|
10913
11572
|
return _contextConnect(Component, namespace, { forwardsRef: true });
|
|
10914
11573
|
}
|
|
@@ -10925,7 +11584,7 @@ function _contextConnect(Component, namespace, options) {
|
|
|
10925
11584
|
});
|
|
10926
11585
|
}
|
|
10927
11586
|
//#endregion
|
|
10928
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
11587
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/utils.mjs
|
|
10929
11588
|
function getNamespace(componentName) {
|
|
10930
11589
|
return { [COMPONENT_NAMESPACE]: componentName };
|
|
10931
11590
|
}
|
|
@@ -10933,7 +11592,7 @@ function getConnectedNamespace() {
|
|
|
10933
11592
|
return { [CONNECTED_NAMESPACE]: true };
|
|
10934
11593
|
}
|
|
10935
11594
|
//#endregion
|
|
10936
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
11595
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/context/use-context-system.mjs
|
|
10937
11596
|
function useContextSystem(props, namespace) {
|
|
10938
11597
|
const contextSystemProps = useComponentsContext();
|
|
10939
11598
|
if (typeof namespace === "undefined") globalThis.SCRIPT_DEBUG === true && warning("useContextSystem: Please provide a namespace");
|
|
@@ -10955,7 +11614,7 @@ function useContextSystem(props, namespace) {
|
|
|
10955
11614
|
return finalComponentProps;
|
|
10956
11615
|
}
|
|
10957
11616
|
//#endregion
|
|
10958
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
11617
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/visually-hidden/styles.mjs
|
|
10959
11618
|
var visuallyHidden = {
|
|
10960
11619
|
border: 0,
|
|
10961
11620
|
clip: "rect(1px, 1px, 1px, 1px)",
|
|
@@ -10971,7 +11630,7 @@ var visuallyHidden = {
|
|
|
10971
11630
|
wordBreak: "normal"
|
|
10972
11631
|
};
|
|
10973
11632
|
//#endregion
|
|
10974
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
11633
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/utils/polymorphic-element.mjs
|
|
10975
11634
|
var customAttributeRegExp = /^(data|aria|x)-/i;
|
|
10976
11635
|
var eventHandlerRegExp = /^on[A-Z]/;
|
|
10977
11636
|
var svgElementNames = new Set(`animate animateMotion animateTransform circle clipPath defs desc ellipse
|
|
@@ -11061,7 +11720,7 @@ function UnforwardedPolymorphicElement({ as, ...props }, ref) {
|
|
|
11061
11720
|
}
|
|
11062
11721
|
var PolymorphicElement = (0, react.forwardRef)(UnforwardedPolymorphicElement);
|
|
11063
11722
|
//#endregion
|
|
11064
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
11723
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/view/component.mjs
|
|
11065
11724
|
function UnforwardedView({ css, ...restProps }, ref) {
|
|
11066
11725
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PolymorphicElement, {
|
|
11067
11726
|
ref,
|
|
@@ -11070,7 +11729,7 @@ function UnforwardedView({ css, ...restProps }, ref) {
|
|
|
11070
11729
|
}
|
|
11071
11730
|
var component_default$2 = Object.assign((0, react.forwardRef)(UnforwardedView), { selector: ".components-view" });
|
|
11072
11731
|
//#endregion
|
|
11073
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
11732
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/visually-hidden/component.mjs
|
|
11074
11733
|
function UnconnectedVisuallyHidden(props, forwardedRef) {
|
|
11075
11734
|
const { style: styleProp, ...contextProps } = useContextSystem(props, "VisuallyHidden");
|
|
11076
11735
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default$2, {
|
|
@@ -11085,7 +11744,7 @@ function UnconnectedVisuallyHidden(props, forwardedRef) {
|
|
|
11085
11744
|
}
|
|
11086
11745
|
var component_default$1 = contextConnect(UnconnectedVisuallyHidden, "VisuallyHidden");
|
|
11087
11746
|
//#endregion
|
|
11088
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
11747
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/utils/use-responsive-value.mjs
|
|
11089
11748
|
var breakpoints = [
|
|
11090
11749
|
"40em",
|
|
11091
11750
|
"52em",
|
|
@@ -11124,7 +11783,7 @@ function useResponsiveValue(values, options = {}) {
|
|
|
11124
11783
|
return array[index >= array.length ? array.length - 1 : index];
|
|
11125
11784
|
}
|
|
11126
11785
|
//#endregion
|
|
11127
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
11786
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/grid/utils.mjs
|
|
11128
11787
|
var ALIGNMENTS = {
|
|
11129
11788
|
bottom: {
|
|
11130
11789
|
alignItems: "flex-end",
|
|
@@ -11172,7 +11831,7 @@ function getAlignmentProps(alignment) {
|
|
|
11172
11831
|
return alignment ? ALIGNMENTS[alignment] ?? {} : {};
|
|
11173
11832
|
}
|
|
11174
11833
|
//#endregion
|
|
11175
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
11834
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/grid/hook.mjs
|
|
11176
11835
|
var STYLE_HASH_ATTRIBUTE = "data-wp-hash";
|
|
11177
11836
|
function getRuntime() {
|
|
11178
11837
|
const globalScope = globalThis;
|
|
@@ -11317,7 +11976,7 @@ function useGrid(props) {
|
|
|
11317
11976
|
};
|
|
11318
11977
|
}
|
|
11319
11978
|
//#endregion
|
|
11320
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@
|
|
11979
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@42.0.0_@date-fns+tz@1.5.0_@types+react-dom@19.3.0_@types+react@19_6d0393a58a031b78f608040e2ecbe6e0/node_modules/@wordpress/components/build-module/grid/component.mjs
|
|
11321
11980
|
function UnconnectedGrid(props, forwardedRef) {
|
|
11322
11981
|
const gridProps = useGrid(props);
|
|
11323
11982
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default$2, {
|
|
@@ -11924,51 +12583,6 @@ const PieSelectionOutput = ({ withTooltips, selectedIndex, selectedData, keyboar
|
|
|
11924
12583
|
] });
|
|
11925
12584
|
};
|
|
11926
12585
|
//#endregion
|
|
11927
|
-
//#region src/charts/private/pie-keyboard-navigation/pie-keyboard-navigation.module.scss
|
|
11928
|
-
var pie_keyboard_navigation_module_default = {
|
|
11929
|
-
"selected-ring__contrast": "a8ccharts-V2HDKq-selected-ring__contrast",
|
|
11930
|
-
"selected-ring__focus": "a8ccharts-V2HDKq-selected-ring__focus"
|
|
11931
|
-
};
|
|
11932
|
-
//#endregion
|
|
11933
|
-
//#region src/charts/private/pie-keyboard-navigation/selected-segment-ring.tsx
|
|
11934
|
-
/**
|
|
11935
|
-
* Encodes `value` for an SVG id, losslessly so distinct chart ids never share a clip path.
|
|
11936
|
-
*
|
|
11937
|
-
* @param value - Any string.
|
|
11938
|
-
* @return `value` with each character outside `[A-Za-z0-9-]` written as `_<hex code point>_`.
|
|
11939
|
-
*/
|
|
11940
|
-
const toSvgId = (value) => Array.from(value, (character) => /[A-Za-z0-9-]/.test(character) ? character : `_${character.codePointAt(0)?.toString(16)}_`).join("");
|
|
11941
|
-
/**
|
|
11942
|
-
* Marks the selected segment with a two-tone ring drawn inside its edge, so the SVG edge cannot cut it off.
|
|
11943
|
-
*
|
|
11944
|
-
* @param {SelectedSegmentRingProps} props - Component props
|
|
11945
|
-
* @return {JSX.Element} The ring
|
|
11946
|
-
*/
|
|
11947
|
-
const SelectedSegmentRing = ({ chartId, d }) => {
|
|
11948
|
-
const clipId = `pie-selected-ring-clip-${toSvgId(chartId)}`;
|
|
11949
|
-
const clipPath = `url(#${clipId})`;
|
|
11950
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", {
|
|
11951
|
-
pointerEvents: "none",
|
|
11952
|
-
"aria-hidden": "true",
|
|
11953
|
-
children: [
|
|
11954
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("clipPath", {
|
|
11955
|
-
id: clipId,
|
|
11956
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d })
|
|
11957
|
-
}),
|
|
11958
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
11959
|
-
d,
|
|
11960
|
-
clipPath,
|
|
11961
|
-
className: pie_keyboard_navigation_module_default["selected-ring__contrast"]
|
|
11962
|
-
}),
|
|
11963
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
11964
|
-
d,
|
|
11965
|
-
clipPath,
|
|
11966
|
-
className: pie_keyboard_navigation_module_default["selected-ring__focus"]
|
|
11967
|
-
})
|
|
11968
|
-
]
|
|
11969
|
-
});
|
|
11970
|
-
};
|
|
11971
|
-
//#endregion
|
|
11972
12586
|
//#region src/charts/private/pie-keyboard-navigation/use-pie-keyboard-navigation.ts
|
|
11973
12587
|
const usePieKeyboardNavigation = ({ navigationArcs, withTooltips, tooltipOffsetX, tooltipOffsetY }) => {
|
|
11974
12588
|
const chartRef = (0, react.useRef)(null);
|
|
@@ -12344,9 +12958,11 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
|
|
|
12344
12958
|
selectedArc && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_group.Group, {
|
|
12345
12959
|
top: centerY,
|
|
12346
12960
|
left: centerX,
|
|
12347
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
12961
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectedRing, {
|
|
12348
12962
|
chartId,
|
|
12349
|
-
d: path(selectedArc) || ""
|
|
12963
|
+
d: path(selectedArc) || "",
|
|
12964
|
+
side: "inside",
|
|
12965
|
+
testId: "pie-selected-ring"
|
|
12350
12966
|
})
|
|
12351
12967
|
}),
|
|
12352
12968
|
!allSegmentsHidden && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_visx_group.Group, {
|
|
@@ -12583,9 +13199,11 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
|
|
|
12583
13199
|
selectedArc && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_group.Group, {
|
|
12584
13200
|
top: height,
|
|
12585
13201
|
left: width / 2,
|
|
12586
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
13202
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectedRing, {
|
|
12587
13203
|
chartId,
|
|
12588
|
-
d: path(selectedArc) || ""
|
|
13204
|
+
d: path(selectedArc) || "",
|
|
13205
|
+
side: "inside",
|
|
13206
|
+
testId: "pie-selected-ring"
|
|
12589
13207
|
})
|
|
12590
13208
|
}),
|
|
12591
13209
|
!allSegmentsHidden && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_visx_group.Group, {
|
|
@@ -12885,6 +13503,8 @@ exports.parseHslString = parseHslString;
|
|
|
12885
13503
|
exports.prefersLightText = prefersLightText;
|
|
12886
13504
|
exports.relativeLuminance = relativeLuminance;
|
|
12887
13505
|
exports.resolveCssVariable = resolveCssVariable;
|
|
13506
|
+
exports.useBarColors = useBarColors;
|
|
13507
|
+
exports.useBarLegendItems = useBarLegendItems;
|
|
12888
13508
|
exports.useCalendarHeatmapData = useCalendarHeatmapData;
|
|
12889
13509
|
exports.useChartFormatting = useChartFormatting;
|
|
12890
13510
|
exports.useChartLegendItems = useChartLegendItems;
|