@automattic/charts 4.2.0 → 4.4.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 +28 -0
- package/dist/index.cjs +533 -127
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -2
- package/dist/index.d.ts +27 -2
- package/dist/index.js +535 -129
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/charts/area-chart/area-chart.tsx +42 -10
- package/src/charts/area-chart/private/validate-data.ts +2 -3
- package/src/charts/area-chart/test/area-chart.test.tsx +72 -1
- package/src/charts/bar-chart/bar-chart.tsx +135 -96
- package/src/charts/bar-chart/index.ts +1 -1
- package/src/charts/bar-chart/private/band-highlight.tsx +3 -1
- package/src/charts/bar-chart/private/band-tooltip.tsx +1 -1
- package/src/charts/bar-chart/private/classified-bar-series.tsx +85 -0
- package/src/charts/bar-chart/private/comparison-bars-geometry.ts +3 -6
- package/src/charts/bar-chart/private/index.ts +2 -0
- package/src/charts/bar-chart/private/test/band-highlight.test.tsx +28 -2
- package/src/charts/bar-chart/private/test/comparison-bars-geometry.test.ts +2 -3
- package/src/charts/bar-chart/private/test/value-domain.test.ts +100 -0
- package/src/charts/bar-chart/private/use-bar-chart-options.ts +12 -39
- package/src/charts/bar-chart/private/value-domain.ts +90 -0
- package/src/charts/bar-chart/test/bar-chart.test.tsx +614 -10
- package/src/charts/bar-chart/types.ts +43 -0
- package/src/charts/line-chart/line-chart.tsx +148 -35
- package/src/charts/line-chart/private/index.ts +1 -0
- package/src/charts/line-chart/private/line-chart-glyph.tsx +17 -3
- package/src/charts/line-chart/private/nearest-pointer-events.tsx +155 -0
- package/src/charts/line-chart/test/line-chart.test.tsx +604 -9
- package/src/charts/line-chart/types.ts +2 -1
- package/src/charts/private/readings.ts +34 -0
- package/src/charts/private/test/readings.test.ts +29 -0
- package/src/charts/private/test/whole-number-ticks.test.tsx +57 -0
- package/src/charts/private/whole-number-ticks.tsx +79 -0
- package/src/components/legend/hooks/test/use-chart-legend-items.test.tsx +124 -0
- package/src/components/legend/hooks/use-chart-legend-items.ts +60 -2
- package/src/components/legend/private/base-legend.tsx +36 -21
- package/src/components/legend/test/legend.test.tsx +84 -0
- package/src/components/legend/types.ts +2 -0
- package/src/components/legend/utils/index.ts +0 -1
- package/src/components/tooltip/accessible-tooltip.tsx +1 -1
- package/src/hooks/test/use-chart-margin.test.tsx +35 -0
- package/src/hooks/use-chart-margin.tsx +7 -2
- package/src/providers/chart-context/global-charts-provider.tsx +1 -1
- package/src/providers/chart-context/test/chart-context.test.tsx +4 -4
- package/src/types.ts +8 -0
- package/src/components/legend/utils/label-transform-factory.ts +0 -24
package/dist/index.cjs
CHANGED
|
@@ -46,6 +46,7 @@ let _visx_gradient = require("@visx/gradient");
|
|
|
46
46
|
let _visx_curve = require("@visx/curve");
|
|
47
47
|
let _visx_responsive = require("@visx/responsive");
|
|
48
48
|
let _visx_annotation = require("@visx/annotation");
|
|
49
|
+
let _visx_vendor_d3_array = require("@visx/vendor/d3-array");
|
|
49
50
|
let _visx_pattern = require("@visx/pattern");
|
|
50
51
|
let react_google_charts = require("react-google-charts");
|
|
51
52
|
let dompurify = require("dompurify");
|
|
@@ -1076,7 +1077,7 @@ const GlobalChartsProvider = ({ children, theme, locale, timeZone }) => {
|
|
|
1076
1077
|
return getChartColor(index, colorCache);
|
|
1077
1078
|
}, [colorCache, groupToColorMap]);
|
|
1078
1079
|
const getElementStyles = (0, react.useCallback)(({ data, index, overrideColor, legendShape }) => {
|
|
1079
|
-
const isSeriesData = data && typeof data === "object" && "data" in data
|
|
1080
|
+
const isSeriesData = data && typeof data === "object" && "data" in data;
|
|
1080
1081
|
const isPointPercentageData = data && typeof data === "object" && "value" in data && typeof data.value === "number" && !("data" in data);
|
|
1081
1082
|
return {
|
|
1082
1083
|
color: resolveColor({
|
|
@@ -1457,8 +1458,9 @@ const useChartMargin = (height, options, data, theme, horizontal = false) => {
|
|
|
1457
1458
|
const allDataPoints = data.flatMap((series) => series.data);
|
|
1458
1459
|
if (horizontal) return allDataPoints.map((d) => d.label || d.date?.getTime());
|
|
1459
1460
|
if (options.axis?.y?.tickValues?.length) return options.axis.y.tickValues;
|
|
1460
|
-
const
|
|
1461
|
-
const
|
|
1461
|
+
const values = allDataPoints.map((d) => d.value).filter((value) => typeof value === "number" && Number.isFinite(value));
|
|
1462
|
+
const minY = values.length ? Math.min(...values) : 0;
|
|
1463
|
+
const maxY = values.length ? Math.max(...values) : 1;
|
|
1462
1464
|
const yScale = (0, _visx_scale.createScale)({
|
|
1463
1465
|
...options.yScale,
|
|
1464
1466
|
domain: options.yScale?.domain ?? [minY, maxY],
|
|
@@ -1793,23 +1795,6 @@ function valueOrIdentityString(_) {
|
|
|
1793
1795
|
return String(valueOrIdentity(_));
|
|
1794
1796
|
}
|
|
1795
1797
|
//#endregion
|
|
1796
|
-
//#region src/components/legend/utils/label-transform-factory.ts
|
|
1797
|
-
/**
|
|
1798
|
-
* Returns a function which takes a Datum and index as input, and returns a formatted label object.
|
|
1799
|
-
* @param {object} root0 - The object to return the value of.
|
|
1800
|
-
* @param {AnyD3Scale} root0.scale - The scale to use.
|
|
1801
|
-
* @param {LabelFormatter<ScaleInput<Scale>>} root0.labelFormat - The label format to use.
|
|
1802
|
-
* @return {ItemTransformer<ScaleInput<Scale>, ReturnType<Scale>>} The label transform factory.
|
|
1803
|
-
*/
|
|
1804
|
-
function labelTransformFactory({ scale, labelFormat }) {
|
|
1805
|
-
return (d, i) => ({
|
|
1806
|
-
datum: d,
|
|
1807
|
-
index: i,
|
|
1808
|
-
text: `${labelFormat(d, i)}`,
|
|
1809
|
-
value: scale(d)
|
|
1810
|
-
});
|
|
1811
|
-
}
|
|
1812
|
-
//#endregion
|
|
1813
1798
|
//#region src/components/legend/private/base-legend.module.scss
|
|
1814
1799
|
var base_legend_module_default = {
|
|
1815
1800
|
"legend": "a8ccharts-04TogW-legend",
|
|
@@ -1851,7 +1836,7 @@ const getLegendItemAriaLabel = (text, value, visible, interactive) => {
|
|
|
1851
1836
|
if (visible) return;
|
|
1852
1837
|
return (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n._x)("%s: hidden", "hidden non-interactive legend item", "jetpack-charts"), accessibleText);
|
|
1853
1838
|
};
|
|
1854
|
-
const BaseLegend = (0, react.forwardRef)(({ items, className, orientation = "horizontal", alignment = "center", shape = "rect", fill = valueOrIdentityString, size = valueOrIdentityString, labelFormat = valueOrIdentity, labelTransform
|
|
1839
|
+
const BaseLegend = (0, react.forwardRef)(({ items, className, orientation = "horizontal", alignment = "center", shape = "rect", fill = valueOrIdentityString, size = valueOrIdentityString, labelFormat = valueOrIdentity, labelTransform, itemStyles, itemClassName, labelStyles, labelClassName, shapeStyles, render, interactive = false, chartId }, ref) => {
|
|
1855
1840
|
const { margin: itemMargin = "0", flexDirection: itemDirection = "row" } = itemStyles ?? {};
|
|
1856
1841
|
const { justifyContent: labelJustifyContent = "flex-start", flex: labelFlex = "0 0 auto", margin: labelMargin = "0 4px", maxWidth, textOverflow = "wrap" } = labelStyles ?? {};
|
|
1857
1842
|
const { width: shapeWidth = 16, height: shapeHeight = 16, margin: shapeMargin = "2px 4px 2px 0" } = shapeStyles ?? {};
|
|
@@ -1859,11 +1844,17 @@ const BaseLegend = (0, react.forwardRef)(({ items, className, orientation = "hor
|
|
|
1859
1844
|
const context = (0, react.useContext)(GlobalChartsContext);
|
|
1860
1845
|
const chartInstanceContext = (0, react.useContext)(ChartInstanceContext);
|
|
1861
1846
|
const standaloneScopeClass = useStandaloneScopeClass();
|
|
1847
|
+
const domain = items.map((item) => item.label);
|
|
1862
1848
|
const legendScale = (0, _visx_scale.scaleOrdinal)({
|
|
1863
|
-
domain
|
|
1849
|
+
domain,
|
|
1864
1850
|
range: items.map((item) => item.color)
|
|
1865
1851
|
});
|
|
1866
|
-
const
|
|
1852
|
+
const defaultLabelTransform = (0, react.useCallback)(({ labelFormat: format }) => (datum, index) => ({
|
|
1853
|
+
datum,
|
|
1854
|
+
index,
|
|
1855
|
+
text: String(format(datum, index)),
|
|
1856
|
+
value: items[index].color
|
|
1857
|
+
}), [items]);
|
|
1867
1858
|
const getShapeStyle = (0, react.useCallback)(({ index }) => items[index]?.shapeStyle, [items]);
|
|
1868
1859
|
const handleLegendClick = (0, react.useCallback)((seriesLabels) => {
|
|
1869
1860
|
if (interactive && chartId && context) {
|
|
@@ -1884,24 +1875,26 @@ const BaseLegend = (0, react.forwardRef)(({ items, className, orientation = "hor
|
|
|
1884
1875
|
chartInstanceContext,
|
|
1885
1876
|
context
|
|
1886
1877
|
]);
|
|
1887
|
-
const createClickHandler = (0, react.useCallback)((seriesLabels) => {
|
|
1888
|
-
if (!
|
|
1878
|
+
const createClickHandler = (0, react.useCallback)((seriesLabels, itemInteractive) => {
|
|
1879
|
+
if (!itemInteractive) return;
|
|
1889
1880
|
return () => handleLegendClick(seriesLabels);
|
|
1890
|
-
}, [
|
|
1891
|
-
const createKeyDownHandler = (0, react.useCallback)((seriesLabels) => {
|
|
1892
|
-
if (!
|
|
1881
|
+
}, [handleLegendClick]);
|
|
1882
|
+
const createKeyDownHandler = (0, react.useCallback)((seriesLabels, itemInteractive) => {
|
|
1883
|
+
if (!itemInteractive) return;
|
|
1893
1884
|
return (event) => {
|
|
1894
1885
|
if (event.key === "Enter" || event.key === " ") {
|
|
1895
1886
|
event.preventDefault();
|
|
1896
1887
|
handleLegendClick(seriesLabels);
|
|
1897
1888
|
}
|
|
1898
1889
|
};
|
|
1899
|
-
}, [
|
|
1890
|
+
}, [handleLegendClick]);
|
|
1900
1891
|
const flexAlignment = ALIGNMENT_TO_FLEX[alignment] ?? "center";
|
|
1892
|
+
const staticItemRole = interactive ? void 0 : "listitem";
|
|
1901
1893
|
return render ? render(items) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_legend.LegendOrdinal, {
|
|
1902
1894
|
scale: legendScale,
|
|
1895
|
+
domain,
|
|
1903
1896
|
labelFormat,
|
|
1904
|
-
labelTransform,
|
|
1897
|
+
labelTransform: labelTransform ?? defaultLabelTransform,
|
|
1905
1898
|
children: (labels) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Stack, {
|
|
1906
1899
|
ref,
|
|
1907
1900
|
direction: orientation === "vertical" ? "column" : "row",
|
|
@@ -1915,19 +1908,20 @@ const BaseLegend = (0, react.forwardRef)(({ items, className, orientation = "hor
|
|
|
1915
1908
|
children: labels.map((label, i) => {
|
|
1916
1909
|
const matchedItem = items[i];
|
|
1917
1910
|
const seriesLabels = matchedItem?.seriesLabels?.length ? matchedItem.seriesLabels : [label.text];
|
|
1918
|
-
const visible = isSeriesVisible(seriesLabels[0]);
|
|
1919
|
-
const
|
|
1920
|
-
const
|
|
1911
|
+
const visible = matchedItem?.interactive === false || isSeriesVisible(seriesLabels[0]);
|
|
1912
|
+
const itemInteractive = interactive && matchedItem?.interactive !== false;
|
|
1913
|
+
const handleClick = createClickHandler(seriesLabels, itemInteractive);
|
|
1914
|
+
const handleKeyDown = createKeyDownHandler(seriesLabels, itemInteractive);
|
|
1921
1915
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_visx_legend.LegendItem, {
|
|
1922
|
-
className: (0, clsx.default)("visx-legend-item", base_legend_module_default["legend-item"],
|
|
1916
|
+
className: (0, clsx.default)("visx-legend-item", base_legend_module_default["legend-item"], itemInteractive && base_legend_module_default["legend-item--interactive"], !visible && base_legend_module_default["legend-item--inactive"], itemClassName),
|
|
1923
1917
|
margin: itemMargin,
|
|
1924
1918
|
flexDirection: orientation === "vertical" && alignment === "end" ? "row-reverse" : itemDirection,
|
|
1925
1919
|
onClick: handleClick,
|
|
1926
1920
|
onKeyDown: handleKeyDown,
|
|
1927
|
-
role:
|
|
1928
|
-
tabIndex:
|
|
1929
|
-
"aria-pressed":
|
|
1930
|
-
"aria-label": getLegendItemAriaLabel(label.text, matchedItem?.value, visible,
|
|
1921
|
+
role: itemInteractive ? "button" : staticItemRole,
|
|
1922
|
+
tabIndex: itemInteractive ? 0 : void 0,
|
|
1923
|
+
"aria-pressed": itemInteractive ? visible : void 0,
|
|
1924
|
+
"aria-label": getLegendItemAriaLabel(label.text, matchedItem?.value, visible, itemInteractive),
|
|
1931
1925
|
children: [items[i]?.renderGlyph ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1932
1926
|
width: items[i]?.glyphSize * 2,
|
|
1933
1927
|
height: items[i]?.glyphSize * 2,
|
|
@@ -2129,6 +2123,32 @@ function processSeriesData(seriesData, getElementStyles, showValues, withGlyph,
|
|
|
2129
2123
|
});
|
|
2130
2124
|
}
|
|
2131
2125
|
/**
|
|
2126
|
+
* Builds the static item that names the comparison overlay, styled like its first series.
|
|
2127
|
+
* Skipped when a comparison series already has an item of its own, so nothing is listed twice.
|
|
2128
|
+
* @param seriesData - The series data to search for a comparison series
|
|
2129
|
+
* @param items - The legend items already built from the series
|
|
2130
|
+
* @param label - The item label
|
|
2131
|
+
* @param getElementStyles - Function to get element styles
|
|
2132
|
+
* @param legendShape - The shape type for legend items (string literal or React component)
|
|
2133
|
+
* @return The legend item, or null when it would add nothing
|
|
2134
|
+
*/
|
|
2135
|
+
function buildComparisonLegendItem(seriesData, items, label, getElementStyles, legendShape) {
|
|
2136
|
+
const itemLabels = new Set(items.map((item) => item.label));
|
|
2137
|
+
const index = seriesData.findIndex((series) => series.options?.type === "comparison" && !itemLabels.has(series.label));
|
|
2138
|
+
if (index === -1) return null;
|
|
2139
|
+
const { color, shapeStyles } = getElementStyles({
|
|
2140
|
+
data: seriesData[index],
|
|
2141
|
+
index,
|
|
2142
|
+
legendShape
|
|
2143
|
+
});
|
|
2144
|
+
return {
|
|
2145
|
+
label,
|
|
2146
|
+
color,
|
|
2147
|
+
shapeStyle: shapeStyles,
|
|
2148
|
+
interactive: false
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
2151
|
+
/**
|
|
2132
2152
|
* Processes point data into legend items
|
|
2133
2153
|
* @param pointData - The point data to process
|
|
2134
2154
|
* @param getElementStyles - Function to get element styles
|
|
@@ -2164,11 +2184,16 @@ function processPointData(pointData, getElementStyles, showValues, legendValueDi
|
|
|
2164
2184
|
* @return Array of legend items ready for display
|
|
2165
2185
|
*/
|
|
2166
2186
|
function useChartLegendItems(data, options = {}, legendShape) {
|
|
2167
|
-
const { showValues = false, legendValueDisplay = "percentage", withGlyph = false, glyphSize = 8, collapseGroups = false, renderGlyph } = options;
|
|
2187
|
+
const { showValues = false, legendValueDisplay = "percentage", withGlyph = false, glyphSize = 8, collapseGroups = false, comparisonItem = false, renderGlyph } = options;
|
|
2168
2188
|
const { getElementStyles } = useGlobalChartsContext();
|
|
2169
2189
|
return (0, react.useMemo)(() => {
|
|
2170
2190
|
if (!data || !Array.isArray(data) || data.length === 0) return [];
|
|
2171
|
-
if ("data" in data[0])
|
|
2191
|
+
if ("data" in data[0]) {
|
|
2192
|
+
const seriesData = data;
|
|
2193
|
+
const items = processSeriesData(seriesData, getElementStyles, showValues, withGlyph, glyphSize, collapseGroups, renderGlyph, legendShape);
|
|
2194
|
+
const comparison = comparisonItem ? buildComparisonLegendItem(seriesData, items, typeof comparisonItem === "string" ? comparisonItem : (0, _wordpress_i18n.__)("Comparison period", "jetpack-charts"), getElementStyles, legendShape) : null;
|
|
2195
|
+
return comparison ? [...items, comparison] : items;
|
|
2196
|
+
}
|
|
2172
2197
|
return processPointData(data, getElementStyles, showValues, legendValueDisplay, withGlyph, glyphSize, renderGlyph, legendShape);
|
|
2173
2198
|
}, [
|
|
2174
2199
|
data,
|
|
@@ -2178,6 +2203,7 @@ function useChartLegendItems(data, options = {}, legendShape) {
|
|
|
2178
2203
|
withGlyph,
|
|
2179
2204
|
glyphSize,
|
|
2180
2205
|
collapseGroups,
|
|
2206
|
+
comparisonItem,
|
|
2181
2207
|
renderGlyph,
|
|
2182
2208
|
legendShape
|
|
2183
2209
|
]);
|
|
@@ -2641,7 +2667,7 @@ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardF
|
|
|
2641
2667
|
tooltipContext?.showTooltip({
|
|
2642
2668
|
datum: tooltipItem.datum,
|
|
2643
2669
|
key: tooltipItem.seriesLabel,
|
|
2644
|
-
index: tooltipItem.
|
|
2670
|
+
index: tooltipItem.dataPointIndex
|
|
2645
2671
|
});
|
|
2646
2672
|
}
|
|
2647
2673
|
}
|
|
@@ -2964,6 +2990,31 @@ const DefaultGlyph = (props) => {
|
|
|
2964
2990
|
});
|
|
2965
2991
|
};
|
|
2966
2992
|
//#endregion
|
|
2993
|
+
//#region src/charts/private/readings.ts
|
|
2994
|
+
/**
|
|
2995
|
+
* Whether a value is a reading a scale can place.
|
|
2996
|
+
*
|
|
2997
|
+
* @param value - The point's value.
|
|
2998
|
+
* @return True for a finite number.
|
|
2999
|
+
*/
|
|
3000
|
+
const isReading = (value) => typeof value === "number" && Number.isFinite(value);
|
|
3001
|
+
/**
|
|
3002
|
+
* Whether a data point's value cannot be drawn.
|
|
3003
|
+
*
|
|
3004
|
+
* @param value - The point's value.
|
|
3005
|
+
* @param options - Validation options.
|
|
3006
|
+
* @param options.allowMissing - Whether `null` is a bucket with no reading rather than a fault.
|
|
3007
|
+
* @return True when the value is invalid.
|
|
3008
|
+
*/
|
|
3009
|
+
const isInvalidReading = (value, { allowMissing }) => value === null ? !allowMissing : value === void 0 || isNaN(value);
|
|
3010
|
+
/**
|
|
3011
|
+
* Formats a reading for a tooltip.
|
|
3012
|
+
*
|
|
3013
|
+
* @param value - The reading, or null when the bucket has none.
|
|
3014
|
+
* @return The formatted value.
|
|
3015
|
+
*/
|
|
3016
|
+
const formatReading = (value) => value == null ? (0, _wordpress_i18n.__)("No data", "jetpack-charts") : (0, _automattic_number_formatters.formatNumber)(value);
|
|
3017
|
+
//#endregion
|
|
2967
3018
|
//#region src/charts/private/center/center.module.scss
|
|
2968
3019
|
var center_module_default = { "center": "a8ccharts-w3qxlG-center" };
|
|
2969
3020
|
//#endregion
|
|
@@ -3336,6 +3387,54 @@ const buildTimeAxisOptions = ({ dataSorted, width, axisOptions, scaleDomain, zoo
|
|
|
3336
3387
|
};
|
|
3337
3388
|
};
|
|
3338
3389
|
//#endregion
|
|
3390
|
+
//#region src/charts/private/whole-number-ticks.tsx
|
|
3391
|
+
/**
|
|
3392
|
+
* Whether every reading in the series is a whole number.
|
|
3393
|
+
*
|
|
3394
|
+
* @param series - The series visx renders.
|
|
3395
|
+
* @return True when at least one reading exists and none has a fraction.
|
|
3396
|
+
*/
|
|
3397
|
+
const hasOnlyWholeNumbers = (series) => {
|
|
3398
|
+
let sawReading = false;
|
|
3399
|
+
for (const { data } of series) for (const point of data) {
|
|
3400
|
+
const value = point?.value;
|
|
3401
|
+
if (!isReading(value)) continue;
|
|
3402
|
+
if (!Number.isInteger(value)) return false;
|
|
3403
|
+
sawReading = true;
|
|
3404
|
+
}
|
|
3405
|
+
return sawReading;
|
|
3406
|
+
};
|
|
3407
|
+
/**
|
|
3408
|
+
* The scale's ticks with the fractional ones dropped.
|
|
3409
|
+
*
|
|
3410
|
+
* d3 steps by 1, 2 or 5 times a power of ten, so every whole number inside the domain is one of its ticks.
|
|
3411
|
+
*
|
|
3412
|
+
* @param scale - The value scale visx built.
|
|
3413
|
+
* @param count - Ticks to ask the scale for; undefined uses the scale's own default, as visx does.
|
|
3414
|
+
* @return Whole-number ticks, or undefined when visx's own ticks need no change.
|
|
3415
|
+
*/
|
|
3416
|
+
const getWholeNumberTickValues = (scale, count) => {
|
|
3417
|
+
const ticks = scale?.ticks?.(count);
|
|
3418
|
+
if (!ticks?.every((tick) => typeof tick === "number")) return;
|
|
3419
|
+
const whole = ticks.filter((tick) => Number.isInteger(tick));
|
|
3420
|
+
return whole.length > 1 && whole.length < ticks.length ? whole : void 0;
|
|
3421
|
+
};
|
|
3422
|
+
/**
|
|
3423
|
+
* Hands its children the whole-number ticks of the chart's value scale.
|
|
3424
|
+
*
|
|
3425
|
+
* @param props - Component props.
|
|
3426
|
+
* @param props.axis - Which scale carries the values.
|
|
3427
|
+
* @param props.numTicks - Ticks to ask the scale for.
|
|
3428
|
+
* @param props.enabled - Whether the plotted values are all whole numbers and the caller pinned neither the ticks nor the value domain.
|
|
3429
|
+
* @param props.children - Renders the axis and grid with the tick values.
|
|
3430
|
+
* @return The rendered children.
|
|
3431
|
+
*/
|
|
3432
|
+
const WholeNumberTicks = ({ axis, numTicks, enabled, children }) => {
|
|
3433
|
+
const { xScale, yScale } = (0, react.useContext)(_visx_xychart.DataContext);
|
|
3434
|
+
const tickValues = enabled ? getWholeNumberTickValues(axis === "x" ? xScale : yScale, numTicks) : void 0;
|
|
3435
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: children(tickValues) });
|
|
3436
|
+
};
|
|
3437
|
+
//#endregion
|
|
3339
3438
|
//#region src/charts/private/with-responsive/with-responsive.module.scss
|
|
3340
3439
|
var with_responsive_module_default = {
|
|
3341
3440
|
"container": "a8ccharts-sP1gHa-container",
|
|
@@ -4002,11 +4101,15 @@ const toNumber$1 = (val) => {
|
|
|
4002
4101
|
const LineChartGlyph = ({ data, index, color, glyphStyle, renderGlyph, accessors, position }) => {
|
|
4003
4102
|
const { xScale, yScale } = (0, react.useContext)(_visx_xychart.DataContext) || {};
|
|
4004
4103
|
if (!xScale || !yScale) return null;
|
|
4005
|
-
|
|
4006
|
-
|
|
4104
|
+
const hasFiniteY = (datum) => {
|
|
4105
|
+
const scaledY = yScale(accessors.yAccessor(datum));
|
|
4106
|
+
return typeof scaledY === "number" && Number.isFinite(scaledY);
|
|
4107
|
+
};
|
|
4108
|
+
const point = position === "start" ? data.data.find(hasFiniteY) : data.data.findLast(hasFiniteY);
|
|
4109
|
+
if (!point) return null;
|
|
4007
4110
|
const x = xScale(accessors.xAccessor(point));
|
|
4008
4111
|
const y = yScale(accessors.yAccessor(point));
|
|
4009
|
-
if (typeof x !== "number" || typeof y !== "number") return null;
|
|
4112
|
+
if (typeof x !== "number" || typeof y !== "number" || !Number.isFinite(x) || !Number.isFinite(y)) return null;
|
|
4010
4113
|
const size = Math.max(0, toNumber$1(glyphStyle?.radius) ?? 4);
|
|
4011
4114
|
return renderGlyph({
|
|
4012
4115
|
key: `${position}-glyph-${data.label}`,
|
|
@@ -4021,6 +4124,106 @@ const LineChartGlyph = ({ data, index, color, glyphStyle, renderGlyph, accessors
|
|
|
4021
4124
|
});
|
|
4022
4125
|
};
|
|
4023
4126
|
//#endregion
|
|
4127
|
+
//#region src/charts/line-chart/private/nearest-pointer-events.tsx
|
|
4128
|
+
/**
|
|
4129
|
+
* Find the datum nearest a pointer along x, in one series sorted by x.
|
|
4130
|
+
*
|
|
4131
|
+
* @param data - The series' registered data, in ascending x order.
|
|
4132
|
+
* @param xAccessor - Reads a datum's x value.
|
|
4133
|
+
* @param xScale - The chart's x scale.
|
|
4134
|
+
* @param x - Pointer x in SVG coordinates.
|
|
4135
|
+
* @return Index of the nearest datum and its distance, or index -1 when none has a position.
|
|
4136
|
+
*/
|
|
4137
|
+
function nearestByX(data, xAccessor, xScale, x) {
|
|
4138
|
+
return (typeof xScale.invert === "function" ? [(0, _visx_vendor_d3_array.bisector)(xAccessor).left(data, xScale.invert(x))].flatMap((index) => [index - 1, index]) : data.map((_datum, index) => index)).filter((index) => index >= 0 && index < data.length).reduce((best, index) => {
|
|
4139
|
+
const distance = Math.abs(Number(xScale(xAccessor(data[index]))) - x);
|
|
4140
|
+
return distance < best.distance ? {
|
|
4141
|
+
index,
|
|
4142
|
+
distance
|
|
4143
|
+
} : best;
|
|
4144
|
+
}, {
|
|
4145
|
+
index: -1,
|
|
4146
|
+
distance: Infinity
|
|
4147
|
+
});
|
|
4148
|
+
}
|
|
4149
|
+
/**
|
|
4150
|
+
* Pick the candidate to report: the closest placed reading, else the closest bucket with no reading along x.
|
|
4151
|
+
*
|
|
4152
|
+
* @param candidates - One candidate per series.
|
|
4153
|
+
* @return The chosen candidate's params, or undefined when there are none.
|
|
4154
|
+
*/
|
|
4155
|
+
function pickNearest(candidates) {
|
|
4156
|
+
const placed = candidates.map(({ params }) => params).filter((params) => Number.isFinite(params.distanceY));
|
|
4157
|
+
const missing = candidates.filter(({ isMissing }) => isMissing).map(({ params }) => params);
|
|
4158
|
+
const distance = (params) => placed.length ? Math.hypot(params.distanceX ?? 0, params.distanceY ?? 0) : params.distanceX ?? 0;
|
|
4159
|
+
return (placed.length ? placed : missing).reduce((best, params) => !best || distance(params) < distance(best) ? params : best, void 0);
|
|
4160
|
+
}
|
|
4161
|
+
/**
|
|
4162
|
+
* Report pointer events at the nearest datum, including a bucket with no reading.
|
|
4163
|
+
*
|
|
4164
|
+
* visx's own nearest search measures y distance too, which is NaN for a null value, so a pointer
|
|
4165
|
+
* over a bucket where no series has a reading fires no event at all.
|
|
4166
|
+
*
|
|
4167
|
+
* @param props - Handlers to call.
|
|
4168
|
+
* @param props.onPointerDown - Receives the nearest datum on pointer down.
|
|
4169
|
+
* @param props.onPointerMove - Receives the nearest datum on pointer move.
|
|
4170
|
+
* @param props.onPointerUp - Receives the nearest datum on pointer up.
|
|
4171
|
+
* @return No visual content.
|
|
4172
|
+
*/
|
|
4173
|
+
function NearestPointerEvents({ onPointerDown, onPointerMove, onPointerUp }) {
|
|
4174
|
+
const { xScale, yScale, dataRegistry } = (0, react.useContext)(_visx_xychart.DataContext);
|
|
4175
|
+
const findNearest = (0, react.useCallback)((params) => {
|
|
4176
|
+
const point = params?.svgPoint;
|
|
4177
|
+
if (!point || !xScale || !yScale || !dataRegistry) return;
|
|
4178
|
+
const candidates = [];
|
|
4179
|
+
for (const key of dataRegistry.keys()) {
|
|
4180
|
+
const entry = dataRegistry.get(key);
|
|
4181
|
+
if (!entry) continue;
|
|
4182
|
+
const { index, distance } = nearestByX(entry.data, entry.xAccessor, xScale, point.x);
|
|
4183
|
+
if (index < 0) continue;
|
|
4184
|
+
const datum = entry.data[index];
|
|
4185
|
+
const value = entry.yAccessor(datum);
|
|
4186
|
+
candidates.push({
|
|
4187
|
+
params: {
|
|
4188
|
+
event: params.event,
|
|
4189
|
+
svgPoint: point,
|
|
4190
|
+
key,
|
|
4191
|
+
datum,
|
|
4192
|
+
index,
|
|
4193
|
+
distanceX: distance,
|
|
4194
|
+
distanceY: Math.abs(Number(yScale(value)) - point.y)
|
|
4195
|
+
},
|
|
4196
|
+
isMissing: value == null
|
|
4197
|
+
});
|
|
4198
|
+
}
|
|
4199
|
+
return pickNearest(candidates);
|
|
4200
|
+
}, [
|
|
4201
|
+
xScale,
|
|
4202
|
+
yScale,
|
|
4203
|
+
dataRegistry
|
|
4204
|
+
]);
|
|
4205
|
+
const handlers = (0, react.useMemo)(() => {
|
|
4206
|
+
const report = (handler) => handler && ((params) => {
|
|
4207
|
+
const nearest = findNearest(params);
|
|
4208
|
+
if (nearest) handler(nearest);
|
|
4209
|
+
});
|
|
4210
|
+
return {
|
|
4211
|
+
pointerdown: report(onPointerDown),
|
|
4212
|
+
pointermove: report(onPointerMove),
|
|
4213
|
+
pointerup: report(onPointerUp)
|
|
4214
|
+
};
|
|
4215
|
+
}, [
|
|
4216
|
+
findNearest,
|
|
4217
|
+
onPointerDown,
|
|
4218
|
+
onPointerMove,
|
|
4219
|
+
onPointerUp
|
|
4220
|
+
]);
|
|
4221
|
+
(0, _visx_xychart.useEventEmitter)("pointerdown", handlers.pointerdown);
|
|
4222
|
+
(0, _visx_xychart.useEventEmitter)("pointermove", handlers.pointermove);
|
|
4223
|
+
(0, _visx_xychart.useEventEmitter)("pointerup", handlers.pointerup);
|
|
4224
|
+
return null;
|
|
4225
|
+
}
|
|
4226
|
+
//#endregion
|
|
4024
4227
|
//#region src/charts/line-chart/line-chart.tsx
|
|
4025
4228
|
const defaultRenderGlyph = (props) => {
|
|
4026
4229
|
return /* @__PURE__ */ (0, react.createElement)(DefaultGlyph, {
|
|
@@ -4064,8 +4267,13 @@ const renderDefaultTooltip = (params, contentStyle) => {
|
|
|
4064
4267
|
if (!nearestDatum) return null;
|
|
4065
4268
|
const tooltipPoints = Object.entries(tooltipData?.datumByKey || {}).map(([key, { datum }]) => ({
|
|
4066
4269
|
key,
|
|
4067
|
-
value: datum.value
|
|
4068
|
-
})).sort((a, b) =>
|
|
4270
|
+
value: datum.value ?? null
|
|
4271
|
+
})).sort((a, b) => {
|
|
4272
|
+
if (a.value === null && b.value === null) return 0;
|
|
4273
|
+
if (a.value === null) return 1;
|
|
4274
|
+
if (b.value === null) return -1;
|
|
4275
|
+
return b.value - a.value;
|
|
4276
|
+
});
|
|
4069
4277
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4070
4278
|
className: line_chart_module_default["line-chart__tooltip"],
|
|
4071
4279
|
style: contentStyle,
|
|
@@ -4085,16 +4293,23 @@ const renderDefaultTooltip = (params, contentStyle) => {
|
|
|
4085
4293
|
children: [point.key, ":"]
|
|
4086
4294
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4087
4295
|
className: line_chart_module_default["line-chart__tooltip-value"],
|
|
4088
|
-
children: (
|
|
4296
|
+
children: formatReading(point.value)
|
|
4089
4297
|
})]
|
|
4090
4298
|
}, point.key))]
|
|
4091
4299
|
});
|
|
4092
4300
|
};
|
|
4093
4301
|
const validateData$4 = (data) => {
|
|
4094
|
-
if (!data?.length) return "No data available";
|
|
4095
|
-
if (data.some((series) => series.data.some((point) =>
|
|
4302
|
+
if (!data?.length) return (0, _wordpress_i18n.__)("No data available", "jetpack-charts");
|
|
4303
|
+
if (data.some((series) => series.data.some((point) => isInvalidReading(point.value, { allowMissing: true }) || "date" in point && point.date && isNaN(point.date.getTime())))) return (0, _wordpress_i18n.__)("Invalid data", "jetpack-charts");
|
|
4096
4304
|
return null;
|
|
4097
4305
|
};
|
|
4306
|
+
const getFallbackYDomain = (readingExtent, isLogScale) => {
|
|
4307
|
+
const emptyDomain = isLogScale ? [1, 10] : [0, 1];
|
|
4308
|
+
if (!readingExtent) return emptyDomain;
|
|
4309
|
+
const [min, max] = readingExtent;
|
|
4310
|
+
if (min !== max || isLogScale) return;
|
|
4311
|
+
return min === 0 ? emptyDomain : [Math.min(0, min), Math.max(0, max)];
|
|
4312
|
+
};
|
|
4098
4313
|
const LineChartScalesRef = ({ chartRef, width, height, margin }) => {
|
|
4099
4314
|
const context = (0, react.useContext)(_visx_xychart.DataContext);
|
|
4100
4315
|
(0, react.useImperativeHandle)(chartRef, () => ({
|
|
@@ -4121,6 +4336,7 @@ const LineChartScalesRef = ({ chartRef, width, height, margin }) => {
|
|
|
4121
4336
|
const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, renderGlyph = defaultRenderGlyph, glyphStyle = {}, withLegendGlyph = false, withGradientFill = false, smoothing = true, curveType, renderTooltip = renderDefaultTooltip, tooltipPlacement, tooltipStyle, withStartGlyphs = false, withEndGlyphs = false, animation, options = {}, onPointerDown = void 0, onPointerUp = void 0, onPointerMove = void 0, onPointerOut = void 0, onDatumActivate = void 0, zoomable = false, rescaleYOnVisibilityChange = true, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
|
|
4122
4337
|
const legendInteractive = legend.interactive ?? false;
|
|
4123
4338
|
const legendCollapseGroups = legend.collapseGroups ?? false;
|
|
4339
|
+
const legendComparisonItem = legend.comparisonItem ?? false;
|
|
4124
4340
|
const legendShape = legend.shape ?? "line";
|
|
4125
4341
|
const legendPosition = legend.position ?? "bottom";
|
|
4126
4342
|
const formatting = useChartFormatting();
|
|
@@ -4167,19 +4383,32 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
4167
4383
|
const allSeriesHidden = (0, react.useMemo)(() => {
|
|
4168
4384
|
return seriesWithVisibility.every(({ isVisible }) => !isVisible);
|
|
4169
4385
|
}, [seriesWithVisibility]);
|
|
4170
|
-
const
|
|
4171
|
-
|
|
4386
|
+
const yDomain = (0, react.useMemo)(() => {
|
|
4387
|
+
const includeZero = options?.yScale?.zero === true && (0, _visx_scale.scaleCanBeZeroed)({ type: options.yScale.type ?? "linear" });
|
|
4388
|
+
if (rescaleYOnVisibilityChange && !includeZero) return;
|
|
4172
4389
|
let min = Infinity;
|
|
4173
4390
|
let max = -Infinity;
|
|
4174
|
-
for (const series of dataSorted)
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4391
|
+
for (const series of dataSorted) {
|
|
4392
|
+
if (rescaleYOnVisibilityChange && !isSeriesVisible(series.label)) continue;
|
|
4393
|
+
for (const point of series.data ?? []) {
|
|
4394
|
+
const value = point?.value;
|
|
4395
|
+
if (isReading(value)) {
|
|
4396
|
+
min = Math.min(min, value);
|
|
4397
|
+
max = Math.max(max, value);
|
|
4398
|
+
}
|
|
4179
4399
|
}
|
|
4180
4400
|
}
|
|
4401
|
+
if (includeZero) {
|
|
4402
|
+
if (min === Infinity) return;
|
|
4403
|
+
return max === 0 && min === 0 ? [0, 1] : [Math.min(0, min), Math.max(0, max)];
|
|
4404
|
+
}
|
|
4181
4405
|
return min < max ? [min, max] : void 0;
|
|
4182
|
-
}, [
|
|
4406
|
+
}, [
|
|
4407
|
+
rescaleYOnVisibilityChange,
|
|
4408
|
+
dataSorted,
|
|
4409
|
+
isSeriesVisible,
|
|
4410
|
+
options?.yScale
|
|
4411
|
+
]);
|
|
4183
4412
|
const activateSelectedPoint = (0, react.useCallback)((index) => {
|
|
4184
4413
|
const series = dataSorted[0];
|
|
4185
4414
|
const datum = series?.data[index];
|
|
@@ -4199,7 +4428,24 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
4199
4428
|
onActivate: activateSelectedPoint,
|
|
4200
4429
|
preventTooltipScroll: tooltipPlacement === "below-axis"
|
|
4201
4430
|
});
|
|
4431
|
+
const visibleReadingExtent = (0, react.useMemo)(() => {
|
|
4432
|
+
let min = Infinity;
|
|
4433
|
+
let max = -Infinity;
|
|
4434
|
+
for (const series of dataSorted) {
|
|
4435
|
+
if (!isSeriesVisible(series.label)) continue;
|
|
4436
|
+
for (const point of series.data) {
|
|
4437
|
+
const value = point?.value;
|
|
4438
|
+
if (isReading(value)) {
|
|
4439
|
+
min = Math.min(min, value);
|
|
4440
|
+
max = Math.max(max, value);
|
|
4441
|
+
}
|
|
4442
|
+
}
|
|
4443
|
+
}
|
|
4444
|
+
return min <= max ? [min, max] : void 0;
|
|
4445
|
+
}, [dataSorted, isSeriesVisible]);
|
|
4446
|
+
const hasWholeNumberValues = (0, react.useMemo)(() => hasOnlyWholeNumbers(dataSorted.filter((series) => isSeriesVisible(series.label))), [dataSorted, isSeriesVisible]);
|
|
4202
4447
|
const chartOptions = (0, react.useMemo)(() => {
|
|
4448
|
+
const fallbackYDomain = getFallbackYDomain(visibleReadingExtent, options?.yScale?.type === "log");
|
|
4203
4449
|
return {
|
|
4204
4450
|
axis: {
|
|
4205
4451
|
x: buildTimeAxisOptions({
|
|
@@ -4228,7 +4474,8 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
4228
4474
|
type: "linear",
|
|
4229
4475
|
nice: true,
|
|
4230
4476
|
zero: false,
|
|
4231
|
-
...
|
|
4477
|
+
...fallbackYDomain ? { domain: fallbackYDomain } : {},
|
|
4478
|
+
...yDomain ? { domain: yDomain } : {},
|
|
4232
4479
|
...options?.yScale
|
|
4233
4480
|
}
|
|
4234
4481
|
};
|
|
@@ -4237,7 +4484,8 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
4237
4484
|
dataSorted,
|
|
4238
4485
|
width,
|
|
4239
4486
|
zoom.domain,
|
|
4240
|
-
|
|
4487
|
+
yDomain,
|
|
4488
|
+
visibleReadingExtent,
|
|
4241
4489
|
formatting,
|
|
4242
4490
|
isSeriesVisible
|
|
4243
4491
|
]);
|
|
@@ -4272,11 +4520,13 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
4272
4520
|
withGlyph: withLegendGlyph,
|
|
4273
4521
|
glyphSize: Math.max(0, toNumber(glyphStyle?.radius) ?? 4),
|
|
4274
4522
|
collapseGroups: legendCollapseGroups,
|
|
4523
|
+
comparisonItem: legendComparisonItem,
|
|
4275
4524
|
renderGlyph
|
|
4276
4525
|
}), [
|
|
4277
4526
|
withLegendGlyph,
|
|
4278
4527
|
glyphStyle?.radius,
|
|
4279
4528
|
legendCollapseGroups,
|
|
4529
|
+
legendComparisonItem,
|
|
4280
4530
|
renderGlyph
|
|
4281
4531
|
]), legendShape);
|
|
4282
4532
|
const chartMetadata = (0, react.useMemo)(() => ({
|
|
@@ -4388,18 +4638,26 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
4388
4638
|
},
|
|
4389
4639
|
xScale: chartOptions.xScale,
|
|
4390
4640
|
yScale: chartOptions.yScale,
|
|
4391
|
-
onPointerDown: zoom.handlers.onPointerDown,
|
|
4392
|
-
onPointerUp: zoom.handlers.onPointerUp,
|
|
4393
|
-
onPointerMove: zoom.handlers.onPointerMove,
|
|
4394
4641
|
onPointerOut,
|
|
4395
|
-
pointerEventsDataKey: "nearest",
|
|
4396
4642
|
children: [
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4643
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(NearestPointerEvents, { ...zoom.handlers }),
|
|
4644
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WholeNumberTicks, {
|
|
4645
|
+
axis: "y",
|
|
4646
|
+
numTicks: chartOptions.axis.y.numTicks,
|
|
4647
|
+
enabled: hasWholeNumberValues && !chartOptions.axis.y.tickValues && !options?.yScale?.domain,
|
|
4648
|
+
children: (tickValues) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4649
|
+
!allSeriesHidden && gridVisibility !== "none" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Grid, {
|
|
4650
|
+
columns: false,
|
|
4651
|
+
numTicks: chartOptions.axis.y.numTicks,
|
|
4652
|
+
tickValues: tickValues ?? chartOptions.axis.y.tickValues
|
|
4653
|
+
}),
|
|
4654
|
+
!allSeriesHidden && chartOptions.axis.x.display && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Axis, { ...chartOptions.axis.x }),
|
|
4655
|
+
!allSeriesHidden && chartOptions.axis.y.display && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Axis, {
|
|
4656
|
+
...chartOptions.axis.y,
|
|
4657
|
+
...tickValues ? { tickValues } : {}
|
|
4658
|
+
})
|
|
4659
|
+
] })
|
|
4400
4660
|
}),
|
|
4401
|
-
!allSeriesHidden && chartOptions.axis.x.display && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Axis, { ...chartOptions.axis.x }),
|
|
4402
|
-
!allSeriesHidden && chartOptions.axis.y.display && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Axis, { ...chartOptions.axis.y }),
|
|
4403
4661
|
allSeriesHidden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SvgEmptyState, {
|
|
4404
4662
|
x: width / 2,
|
|
4405
4663
|
y: chartHeight / 2,
|
|
@@ -4540,7 +4798,7 @@ var area_chart_module_default = {
|
|
|
4540
4798
|
const validateData$3 = (data) => {
|
|
4541
4799
|
if (!data?.length) return (0, _wordpress_i18n.__)("No data available", "jetpack-charts");
|
|
4542
4800
|
if (data.some((series) => !series.data?.length)) return (0, _wordpress_i18n.__)("No data available", "jetpack-charts");
|
|
4543
|
-
if (data.some((series) => series.data.some((point) =>
|
|
4801
|
+
if (data.some((series) => series.data.some((point) => isInvalidReading(point.value, { allowMissing: false }) || "date" in point && point.date && isNaN(point.date.getTime())))) return (0, _wordpress_i18n.__)("Invalid data", "jetpack-charts");
|
|
4544
4802
|
return null;
|
|
4545
4803
|
};
|
|
4546
4804
|
//#endregion
|
|
@@ -4656,6 +4914,12 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
4656
4914
|
}));
|
|
4657
4915
|
}, [dataSorted, hiddenSeries]);
|
|
4658
4916
|
const allSeriesHidden = (0, react.useMemo)(() => seriesWithVisibility.every(({ isVisible }) => !isVisible), [seriesWithVisibility]);
|
|
4917
|
+
const hasWholeNumberValues = (0, react.useMemo)(() => (!stacked || stackOffset === "none") && hasOnlyWholeNumbers(dataSorted.filter((series) => isSeriesVisible(series.label))), [
|
|
4918
|
+
dataSorted,
|
|
4919
|
+
isSeriesVisible,
|
|
4920
|
+
stacked,
|
|
4921
|
+
stackOffset
|
|
4922
|
+
]);
|
|
4659
4923
|
const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
|
|
4660
4924
|
selectedIndex,
|
|
4661
4925
|
setSelectedIndex,
|
|
@@ -4748,8 +5012,9 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
4748
5012
|
const legendItems = useChartLegendItems(dataSorted, (0, react.useMemo)(() => ({
|
|
4749
5013
|
withGlyph: false,
|
|
4750
5014
|
glyphSize: 0,
|
|
4751
|
-
collapseGroups: legend.collapseGroups ?? false
|
|
4752
|
-
|
|
5015
|
+
collapseGroups: legend.collapseGroups ?? false,
|
|
5016
|
+
comparisonItem: legend.comparisonItem ?? false
|
|
5017
|
+
}), [legend.collapseGroups, legend.comparisonItem]), legendShape);
|
|
4753
5018
|
const chartMetadata = (0, react.useMemo)(() => ({
|
|
4754
5019
|
stacked,
|
|
4755
5020
|
stackOffset,
|
|
@@ -4899,12 +5164,23 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
4899
5164
|
onPointerOut,
|
|
4900
5165
|
pointerEventsDataKey: "nearest",
|
|
4901
5166
|
children: [
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
numTicks:
|
|
5167
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WholeNumberTicks, {
|
|
5168
|
+
axis: "y",
|
|
5169
|
+
numTicks: chartOptions.axis.y.numTicks,
|
|
5170
|
+
enabled: hasWholeNumberValues && !chartOptions.axis.y.tickValues && !options?.yScale?.domain,
|
|
5171
|
+
children: (tickValues) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
5172
|
+
!allSeriesHidden && gridVisibility !== "none" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Grid, {
|
|
5173
|
+
columns: false,
|
|
5174
|
+
numTicks: chartOptions.axis.y.numTicks,
|
|
5175
|
+
tickValues: tickValues ?? chartOptions.axis.y.tickValues
|
|
5176
|
+
}),
|
|
5177
|
+
!allSeriesHidden && chartOptions.axis.x.display && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Axis, { ...chartOptions.axis.x }),
|
|
5178
|
+
!allSeriesHidden && chartOptions.axis.y.display && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Axis, {
|
|
5179
|
+
...chartOptions.axis.y,
|
|
5180
|
+
...tickValues ? { tickValues } : {}
|
|
5181
|
+
})
|
|
5182
|
+
] })
|
|
4905
5183
|
}),
|
|
4906
|
-
!allSeriesHidden && chartOptions.axis.x.display && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Axis, { ...chartOptions.axis.x }),
|
|
4907
|
-
!allSeriesHidden && chartOptions.axis.y.display && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Axis, { ...chartOptions.axis.y }),
|
|
4908
5184
|
allSeriesHidden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SvgEmptyState, {
|
|
4909
5185
|
x: width / 2,
|
|
4910
5186
|
y: chartHeight / 2,
|
|
@@ -5093,6 +5369,65 @@ const createTruncatedTickComponent = (axis) => (props) => {
|
|
|
5093
5369
|
const TruncatedXTickComponent = createTruncatedTickComponent("x");
|
|
5094
5370
|
const TruncatedYTickComponent = createTruncatedTickComponent("y");
|
|
5095
5371
|
//#endregion
|
|
5372
|
+
//#region src/charts/bar-chart/private/value-domain.ts
|
|
5373
|
+
/** Domain for a value axis that has nothing to scale, so its ticks still read 0 to 1. */
|
|
5374
|
+
const EMPTY_DOMAIN = [0, 1];
|
|
5375
|
+
/**
|
|
5376
|
+
* The value a bar is drawn at, preferring `visualValue` so a zero still shows a sliver.
|
|
5377
|
+
*
|
|
5378
|
+
* A missing reading is NaN, not null: `ComparisonBars` coerces the value with `Number()`,
|
|
5379
|
+
* which turns null into 0 and would draw a comparison shadow at the baseline.
|
|
5380
|
+
*
|
|
5381
|
+
* @param point - A data point handed to visx.
|
|
5382
|
+
* @return The bar value, or NaN when the bucket has no reading.
|
|
5383
|
+
*/
|
|
5384
|
+
const getBarValue = (point) => {
|
|
5385
|
+
const enhanced = point;
|
|
5386
|
+
return (enhanced?.visualValue !== void 0 ? enhanced.visualValue : point?.value) ?? NaN;
|
|
5387
|
+
};
|
|
5388
|
+
/**
|
|
5389
|
+
* Whether visx renders a bar for a point.
|
|
5390
|
+
*
|
|
5391
|
+
* @param point - A data point handed to visx.
|
|
5392
|
+
* @return True when the point has a finite bar value.
|
|
5393
|
+
*/
|
|
5394
|
+
const isBarRendered = (point) => Number.isFinite(getBarValue(point));
|
|
5395
|
+
/**
|
|
5396
|
+
* How many bars visx renders for a list of points.
|
|
5397
|
+
*
|
|
5398
|
+
* @param points - Data points handed to visx.
|
|
5399
|
+
* @return The number of points that get a bar.
|
|
5400
|
+
*/
|
|
5401
|
+
const countRenderedBars = (points) => points.filter(isBarRendered).length;
|
|
5402
|
+
/**
|
|
5403
|
+
* An explicit domain for the value scale, or null to let visx fit one to the data.
|
|
5404
|
+
*
|
|
5405
|
+
* Zero goes into the domain here rather than through the scale's `zero` flag: visx applies
|
|
5406
|
+
* `nice` before `zero`, which would leave the axis topping out at a raw maximum.
|
|
5407
|
+
*
|
|
5408
|
+
* @param data - Every series handed to the chart.
|
|
5409
|
+
* @param includeZero - Whether the domain must span zero.
|
|
5410
|
+
* @param isSeriesRendered - Whether visx mounts a series, i.e. the legend shows it.
|
|
5411
|
+
* @return Value domain, or null when visx should fit its own.
|
|
5412
|
+
*/
|
|
5413
|
+
const getValueScaleDomain = (data, includeZero, isSeriesRendered) => {
|
|
5414
|
+
let min = Infinity;
|
|
5415
|
+
let max = -Infinity;
|
|
5416
|
+
for (const series of data) {
|
|
5417
|
+
if (!isSeriesRendered(series)) continue;
|
|
5418
|
+
for (const point of series.data) {
|
|
5419
|
+
const value = getBarValue(point);
|
|
5420
|
+
if (!Number.isFinite(value)) continue;
|
|
5421
|
+
min = Math.min(min, value);
|
|
5422
|
+
max = Math.max(max, value);
|
|
5423
|
+
}
|
|
5424
|
+
}
|
|
5425
|
+
if (min === Infinity) return [...EMPTY_DOMAIN];
|
|
5426
|
+
if (min === max) return min === 0 ? [...EMPTY_DOMAIN] : [Math.min(0, min), Math.max(0, max)];
|
|
5427
|
+
if (includeZero) return [Math.min(0, min), Math.max(0, max)];
|
|
5428
|
+
return null;
|
|
5429
|
+
};
|
|
5430
|
+
//#endregion
|
|
5096
5431
|
//#region src/charts/bar-chart/private/use-bar-chart-options.ts
|
|
5097
5432
|
/** Outer padding of the category band scale (space at the chart edges). */
|
|
5098
5433
|
const BASE_BAND_PADDING = .2;
|
|
@@ -5217,10 +5552,6 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
|
|
|
5217
5552
|
const tooltipDatumFormatter = hasLabels ? labelFormatter : byBucket(getTooltipFormatter(data, tickResolution, formatting));
|
|
5218
5553
|
const valueFormatter = _automattic_number_formatters.formatNumberCompact;
|
|
5219
5554
|
const bandDomain = timeTickFormatter ? getBandDomain(data, isSeriesRendered) : null;
|
|
5220
|
-
const valueAccessor = (d) => {
|
|
5221
|
-
const enhancedPoint = d;
|
|
5222
|
-
return enhancedPoint?.visualValue !== void 0 ? enhancedPoint.visualValue : d?.value;
|
|
5223
|
-
};
|
|
5224
5555
|
return {
|
|
5225
5556
|
timeAxis: bandDomain && timeTickFormatter && {
|
|
5226
5557
|
domain: bandDomain,
|
|
@@ -5231,7 +5562,7 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
|
|
|
5231
5562
|
yTickFormat: valueFormatter,
|
|
5232
5563
|
tooltipLabelFormatter: tooltipDatumFormatter,
|
|
5233
5564
|
xAccessor: bucketAccessor,
|
|
5234
|
-
yAccessor:
|
|
5565
|
+
yAccessor: getBarValue,
|
|
5235
5566
|
gridVisibility: "x",
|
|
5236
5567
|
xScale: bandScale,
|
|
5237
5568
|
yScale: linearScale
|
|
@@ -5240,7 +5571,7 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
|
|
|
5240
5571
|
xTickFormat: valueFormatter,
|
|
5241
5572
|
yTickFormat: labelFormatter,
|
|
5242
5573
|
tooltipLabelFormatter: tooltipDatumFormatter,
|
|
5243
|
-
xAccessor:
|
|
5574
|
+
xAccessor: getBarValue,
|
|
5244
5575
|
yAccessor: bucketAccessor,
|
|
5245
5576
|
gridVisibility: "y",
|
|
5246
5577
|
xScale: linearScale,
|
|
@@ -5255,20 +5586,10 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
|
|
|
5255
5586
|
]);
|
|
5256
5587
|
return (0, react.useMemo)(() => {
|
|
5257
5588
|
const { xTickFormat: defaultXTickFormat, yTickFormat: defaultYTickFormat, tooltipLabelFormatter: defaultTooltipLabelFormatter, xAccessor, yAccessor, gridVisibility, xScale: baseXScale, yScale: baseYScale } = defaultOptions[horizontal ? "horizontal" : "vertical"];
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
data.forEach((series) => {
|
|
5263
|
-
series.data.forEach((d) => {
|
|
5264
|
-
const enhanced = d;
|
|
5265
|
-
const v = enhanced.visualValue !== void 0 ? enhanced.visualValue : d.value;
|
|
5266
|
-
if (typeof v === "number" && Number.isFinite(v)) allValues.push(v);
|
|
5267
|
-
});
|
|
5268
|
-
});
|
|
5269
|
-
if (allValues.length > 0) valueScaleDomainOverride = { domain: [Math.min(0, ...allValues), Math.max(0, ...allValues)] };
|
|
5270
|
-
}
|
|
5271
|
-
}
|
|
5589
|
+
const valueScaleOptions = horizontal ? stableOptions.xScale : stableOptions.yScale;
|
|
5590
|
+
const includeZero = data.some((s) => s.options?.type === "comparison") || valueScaleOptions?.zero !== false;
|
|
5591
|
+
const domain = valueScaleOptions?.domain ? null : getValueScaleDomain(data, includeZero, isSeriesRendered);
|
|
5592
|
+
const valueScaleDomainOverride = domain ? { domain } : {};
|
|
5272
5593
|
const xScale = {
|
|
5273
5594
|
...baseXScale,
|
|
5274
5595
|
...stableOptions.xScale || {},
|
|
@@ -5319,7 +5640,8 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
|
|
|
5319
5640
|
axisConfig,
|
|
5320
5641
|
stableOptions,
|
|
5321
5642
|
horizontal,
|
|
5322
|
-
data
|
|
5643
|
+
data,
|
|
5644
|
+
isSeriesRendered
|
|
5323
5645
|
]);
|
|
5324
5646
|
}
|
|
5325
5647
|
//#endregion
|
|
@@ -5339,9 +5661,8 @@ const createGroupScale = (keys, bandwidth, padding) => (0, _visx_scale.scaleBand
|
|
|
5339
5661
|
//#endregion
|
|
5340
5662
|
//#region src/charts/bar-chart/private/comparison-bars-geometry.ts
|
|
5341
5663
|
/**
|
|
5342
|
-
*
|
|
5343
|
-
*
|
|
5344
|
-
* sit on the same baseline as primary bars.
|
|
5664
|
+
* Mirrors visx's getScaleBaseline so comparison shadows and primary bars
|
|
5665
|
+
* share the same baseline, including out-of-domain zero values.
|
|
5345
5666
|
*
|
|
5346
5667
|
* @param {ValueScale} scale - The continuous value scale.
|
|
5347
5668
|
* @return {number} The baseline output position in pixels.
|
|
@@ -5352,7 +5673,7 @@ function getValueScaleBaseline(scale) {
|
|
|
5352
5673
|
const maybeZero = scale(0);
|
|
5353
5674
|
const [minOutput, maxOutput] = isDescending ? [b, a] : [a, b];
|
|
5354
5675
|
if (isDescending) return Number.isFinite(maybeZero) ? Math.min(Math.max(minOutput, maybeZero), maxOutput) : maxOutput;
|
|
5355
|
-
return Number.isFinite(maybeZero) ? Math.
|
|
5676
|
+
return Number.isFinite(maybeZero) ? Math.max(maybeZero, minOutput) : minOutput;
|
|
5356
5677
|
}
|
|
5357
5678
|
/**
|
|
5358
5679
|
* Compute the rect for a comparison "shadow" bar, centered on the paired
|
|
@@ -5388,6 +5709,51 @@ function computeComparisonRect(params) {
|
|
|
5388
5709
|
height: valueLength
|
|
5389
5710
|
};
|
|
5390
5711
|
}
|
|
5712
|
+
//#endregion
|
|
5713
|
+
//#region src/charts/bar-chart/private/classified-bar-series.tsx
|
|
5714
|
+
const ClassifiedBarSeries = ({ barClassName, primaryKeys, groupPadding, ...props }) => {
|
|
5715
|
+
const { xScale, yScale, horizontal } = (0, react.useContext)(_visx_xychart.DataContext);
|
|
5716
|
+
const bandScale = horizontal ? yScale : xScale;
|
|
5717
|
+
const valueScale = horizontal ? xScale : yScale;
|
|
5718
|
+
const groupScale = (0, react.useMemo)(() => createGroupScale(primaryKeys, bandScale?.bandwidth?.() ?? 0, groupPadding), [
|
|
5719
|
+
primaryKeys,
|
|
5720
|
+
bandScale,
|
|
5721
|
+
groupPadding
|
|
5722
|
+
]);
|
|
5723
|
+
const renderGlyph = (0, react.useCallback)(({ datum, x, y, color, onBlur, onFocus, onPointerMove, onPointerOut, onPointerUp }) => {
|
|
5724
|
+
if (!bandScale?.bandwidth || !valueScale) return null;
|
|
5725
|
+
const baseline = getValueScaleBaseline(valueScale);
|
|
5726
|
+
const position = Number(bandScale((horizontal ? props.yAccessor : props.xAccessor)(datum))) + (groupScale(props.dataKey) ?? 0);
|
|
5727
|
+
const value = horizontal ? x : y;
|
|
5728
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
5729
|
+
className: (0, clsx.default)("visx-bar", barClassName(datum)),
|
|
5730
|
+
tabIndex: onFocus || onBlur ? 0 : void 0,
|
|
5731
|
+
onBlur,
|
|
5732
|
+
onFocus,
|
|
5733
|
+
onPointerMove,
|
|
5734
|
+
onPointerOut,
|
|
5735
|
+
onPointerUp,
|
|
5736
|
+
x: horizontal ? baseline + Math.min(0, value - baseline) : position,
|
|
5737
|
+
y: horizontal ? position : baseline + Math.min(0, value - baseline),
|
|
5738
|
+
width: horizontal ? Math.abs(value - baseline) : groupScale.bandwidth(),
|
|
5739
|
+
height: horizontal ? groupScale.bandwidth() : Math.abs(value - baseline),
|
|
5740
|
+
fill: color
|
|
5741
|
+
});
|
|
5742
|
+
}, [
|
|
5743
|
+
bandScale,
|
|
5744
|
+
valueScale,
|
|
5745
|
+
horizontal,
|
|
5746
|
+
groupScale,
|
|
5747
|
+
props.dataKey,
|
|
5748
|
+
props.xAccessor,
|
|
5749
|
+
props.yAccessor,
|
|
5750
|
+
barClassName
|
|
5751
|
+
]);
|
|
5752
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.GlyphSeries, {
|
|
5753
|
+
...props,
|
|
5754
|
+
renderGlyph
|
|
5755
|
+
});
|
|
5756
|
+
};
|
|
5391
5757
|
/**
|
|
5392
5758
|
* Upper clamp on the computed group padding, so bars can never collapse to zero width
|
|
5393
5759
|
* even at very large `widthFactor` values.
|
|
@@ -5484,6 +5850,8 @@ function BandHighlight({ visible, horizontal, onChange }) {
|
|
|
5484
5850
|
if (position === void 0 || !Number.isFinite(position)) return null;
|
|
5485
5851
|
return {
|
|
5486
5852
|
datum: nearest.datum,
|
|
5853
|
+
key: nearest.key,
|
|
5854
|
+
index: nearest.index,
|
|
5487
5855
|
x: horizontal ? margin?.left ?? 0 : position,
|
|
5488
5856
|
y: horizontal ? position : margin?.top ?? 0,
|
|
5489
5857
|
width: horizontal ? innerWidth : scale.bandwidth(),
|
|
@@ -5620,8 +5988,8 @@ function BandTooltip({ keys, groupPadding, withTooltips, onPointerDown, onPointe
|
|
|
5620
5988
|
//#endregion
|
|
5621
5989
|
//#region src/charts/bar-chart/bar-chart.tsx
|
|
5622
5990
|
const validateData$2 = (data) => {
|
|
5623
|
-
if (!data?.length) return "No data available";
|
|
5624
|
-
if (data.some((series) => series.data.some((point) =>
|
|
5991
|
+
if (!data?.length) return (0, _wordpress_i18n.__)("No data available", "jetpack-charts");
|
|
5992
|
+
if (data.some((series) => series.data.some((point) => isInvalidReading(point.value, { allowMissing: true }) || !point.label && (!("date" in point && point.date) || isNaN(point.date.getTime()))))) return (0, _wordpress_i18n.__)("Invalid data", "jetpack-charts");
|
|
5625
5993
|
return null;
|
|
5626
5994
|
};
|
|
5627
5995
|
const getPatternId = (chartId, index) => `bar-pattern-${chartId}-${index}`;
|
|
@@ -5629,9 +5997,11 @@ const renderTooltipRow = (label, value) => /* @__PURE__ */ (0, react_jsx_runtime
|
|
|
5629
5997
|
className: bar_chart_module_default["bar-chart__tooltip-row"],
|
|
5630
5998
|
children: (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n.__)("%1$s: %2$s", "jetpack-charts"), label, value)
|
|
5631
5999
|
});
|
|
5632
|
-
const BarChartInternal = ({ data, chartId: providedChartId, width, height, className, margin, withTooltips = false, showLegend = false, legend = {}, gridVisibility: gridVisibilityProp, renderTooltip, tooltipPlacement, tooltipAnchorTop, options = {}, orientation = "vertical", withPatterns = false, showZeroValues = false, withBandHighlight = false, onBandHighlightChange, defaultHiddenSeries, animation, children, gap = "md", onPointerDown, onPointerUp, onDatumActivate }) => {
|
|
6000
|
+
const BarChartInternal = ({ data, 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 }) => {
|
|
6001
|
+
if (!withTooltips && (withBandHighlight || onBandHighlightChange)) warnOnce("bar-chart-band-highlight-without-tooltips", "BarChart: withBandHighlight and onBandHighlightChange require withTooltips.");
|
|
5633
6002
|
const legendInteractive = legend.interactive ?? false;
|
|
5634
6003
|
const legendCollapseGroups = legend.collapseGroups ?? false;
|
|
6004
|
+
const legendComparisonItem = legend.comparisonItem ?? false;
|
|
5635
6005
|
const horizontal = orientation === "horizontal";
|
|
5636
6006
|
const chartId = useChartId(providedChartId);
|
|
5637
6007
|
const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
|
|
@@ -5642,10 +6012,21 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
5642
6012
|
enabled: showZeroValues,
|
|
5643
6013
|
valueAxisLength: horizontal ? width : height
|
|
5644
6014
|
});
|
|
5645
|
-
const legendItems = useChartLegendItems(dataSorted, (0, react.useMemo)(() => ({
|
|
6015
|
+
const legendItems = useChartLegendItems(dataSorted, (0, react.useMemo)(() => ({
|
|
6016
|
+
collapseGroups: legendCollapseGroups,
|
|
6017
|
+
comparisonItem: legendComparisonItem
|
|
6018
|
+
}), [legendCollapseGroups, legendComparisonItem]));
|
|
5646
6019
|
const { getElementStyles } = useGlobalChartsContext();
|
|
5647
6020
|
const isSeriesRendered = (0, react.useCallback)((series) => isSeriesVisible(series.label), [isSeriesVisible]);
|
|
6021
|
+
const hasWholeNumberValues = (0, react.useMemo)(() => hasOnlyWholeNumbers(dataSorted.filter(isSeriesRendered)), [dataSorted, isSeriesRendered]);
|
|
5648
6022
|
const chartOptions = useBarChartOptions(dataWithVisibleZeros, horizontal, options, isSeriesRendered);
|
|
6023
|
+
const valueAxis = horizontal ? chartOptions.axis.x : chartOptions.axis.y;
|
|
6024
|
+
const callerValueDomain = horizontal ? options.xScale?.domain : options.yScale?.domain;
|
|
6025
|
+
const wholeNumberTicksProps = {
|
|
6026
|
+
axis: horizontal ? "x" : "y",
|
|
6027
|
+
numTicks: valueAxis.numTicks,
|
|
6028
|
+
enabled: hasWholeNumberValues && !valueAxis.tickValues && !callerValueDomain
|
|
6029
|
+
};
|
|
5649
6030
|
const defaultMargin = useChartMargin(height, chartOptions, dataSorted, theme, horizontal);
|
|
5650
6031
|
const chartRef = (0, react.useRef)(null);
|
|
5651
6032
|
const { legendChildren, nonLegendChildren } = useChartChildren(children, "BarChart");
|
|
@@ -5778,15 +6159,15 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
5778
6159
|
const p = point;
|
|
5779
6160
|
return nearestDatum.label != null ? p.label === nearestDatum.label : !!nearestDatum.date && !!p.date && p.date.getTime() === nearestDatum.date.getTime();
|
|
5780
6161
|
});
|
|
5781
|
-
if (comparisonEntry && comparisonDatum
|
|
6162
|
+
if (comparisonEntry && comparisonDatum) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5782
6163
|
className: bar_chart_module_default["bar-chart__tooltip"],
|
|
5783
6164
|
children: [
|
|
5784
6165
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5785
6166
|
className: bar_chart_module_default["bar-chart__tooltip-header"],
|
|
5786
6167
|
children: categoryLabel
|
|
5787
6168
|
}),
|
|
5788
|
-
renderTooltipRow(primaryKey, (
|
|
5789
|
-
renderTooltipRow(comparisonEntry.series.label, (
|
|
6169
|
+
renderTooltipRow(primaryKey, formatReading(nearestDatum.value)),
|
|
6170
|
+
renderTooltipRow(comparisonEntry.series.label, formatReading(comparisonDatum.value))
|
|
5790
6171
|
]
|
|
5791
6172
|
});
|
|
5792
6173
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -5794,7 +6175,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
5794
6175
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5795
6176
|
className: bar_chart_module_default["bar-chart__tooltip-header"],
|
|
5796
6177
|
children: primaryKey
|
|
5797
|
-
}), renderTooltipRow(categoryLabel, (
|
|
6178
|
+
}), renderTooltipRow(categoryLabel, formatReading(nearestDatum.value))]
|
|
5798
6179
|
});
|
|
5799
6180
|
}, [chartOptions.tooltip, comparisonEntries]);
|
|
5800
6181
|
const renderPattern = (0, react.useCallback)((index, color) => {
|
|
@@ -5845,15 +6226,15 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
5845
6226
|
const createKeyboardHighlightStyle = (0, react.useCallback)(() => {
|
|
5846
6227
|
if (selectedIndex === void 0) return "";
|
|
5847
6228
|
const primaryCount = primaryEntries.length;
|
|
5848
|
-
|
|
6229
|
+
if (!primaryCount) return "";
|
|
5849
6230
|
const dataPointIndex = Math.floor(selectedIndex / primaryCount);
|
|
5850
6231
|
const seriesIndex = selectedIndex % primaryCount;
|
|
5851
|
-
if (dataPointIndex >= maxDataPoints || seriesIndex >= primaryCount) return "";
|
|
5852
6232
|
const seriesData = primaryEntries[seriesIndex]?.series;
|
|
5853
|
-
|
|
5854
|
-
|
|
6233
|
+
const datum = seriesData?.data[dataPointIndex];
|
|
6234
|
+
if (!seriesData || !datum || !isBarRendered(datum)) return "";
|
|
6235
|
+
const domBarIndex = primaryEntries.slice(0, seriesIndex).reduce((count, { series }) => count + countRenderedBars(series.data), 0) + countRenderedBars(seriesData.data.slice(0, dataPointIndex));
|
|
5855
6236
|
return `
|
|
5856
|
-
.bar-chart[data-chart-id="bar-chart-${chartId}"] .visx-bar-group .visx-bar:nth-child(${
|
|
6237
|
+
.bar-chart[data-chart-id="bar-chart-${chartId}"] .visx-bar-group .visx-bar:nth-child(${domBarIndex + 1}) {
|
|
5857
6238
|
stroke: #005fcc;
|
|
5858
6239
|
stroke-width: 2px;
|
|
5859
6240
|
}
|
|
@@ -5947,17 +6328,20 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
5947
6328
|
horizontal,
|
|
5948
6329
|
onChange: onBandHighlightChange
|
|
5949
6330
|
}),
|
|
5950
|
-
!allSeriesHidden && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
6331
|
+
!allSeriesHidden && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WholeNumberTicks, {
|
|
6332
|
+
...wholeNumberTicksProps,
|
|
6333
|
+
children: (valueTicks) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Grid, {
|
|
6334
|
+
columns: gridVisibility.includes("y"),
|
|
6335
|
+
rows: false,
|
|
6336
|
+
numTicks: chartOptions.axis.x.numTicks,
|
|
6337
|
+
tickValues: (horizontal ? valueTicks : void 0) ?? chartOptions.axis.x.tickValues
|
|
6338
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Grid, {
|
|
6339
|
+
columns: false,
|
|
6340
|
+
rows: gridVisibility.includes("x"),
|
|
6341
|
+
numTicks: chartOptions.axis.y.numTicks,
|
|
6342
|
+
tickValues: (horizontal ? void 0 : valueTicks) ?? chartOptions.axis.y.tickValues
|
|
6343
|
+
})] })
|
|
6344
|
+
}),
|
|
5961
6345
|
withPatterns && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("defs", { children: dataSorted.map((seriesData, index) => renderPattern(index, getElementStyles({
|
|
5962
6346
|
data: seriesData,
|
|
5963
6347
|
index
|
|
@@ -5983,7 +6367,19 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
5983
6367
|
getElementStyles,
|
|
5984
6368
|
resolveFill: resolveComparisonFill
|
|
5985
6369
|
}),
|
|
5986
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
6370
|
+
barClassName ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", {
|
|
6371
|
+
className: "visx-bar-group",
|
|
6372
|
+
children: primaryEntries.map(({ series: seriesData, index }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClassifiedBarSeries, {
|
|
6373
|
+
dataKey: seriesData.label,
|
|
6374
|
+
data: seriesData.data,
|
|
6375
|
+
xAccessor: chartOptions.accessors.xAccessor,
|
|
6376
|
+
yAccessor: chartOptions.accessors.yAccessor,
|
|
6377
|
+
colorAccessor: getBarBackground(index),
|
|
6378
|
+
barClassName,
|
|
6379
|
+
primaryKeys,
|
|
6380
|
+
groupPadding
|
|
6381
|
+
}, seriesData.label))
|
|
6382
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.BarGroup, {
|
|
5987
6383
|
padding: groupPadding,
|
|
5988
6384
|
children: primaryEntries.map(({ series: seriesData, index }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.BarSeries, {
|
|
5989
6385
|
dataKey: seriesData?.label,
|
|
@@ -6000,10 +6396,20 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
6000
6396
|
onPointerDown,
|
|
6001
6397
|
onPointerUp
|
|
6002
6398
|
}),
|
|
6003
|
-
!allSeriesHidden && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
6399
|
+
!allSeriesHidden && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WholeNumberTicks, {
|
|
6400
|
+
...wholeNumberTicksProps,
|
|
6401
|
+
children: (valueTicks) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Axis, {
|
|
6402
|
+
...chartOptions.axis.x,
|
|
6403
|
+
...horizontal && valueTicks ? { tickValues: valueTicks } : {}
|
|
6404
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_xychart.Axis, {
|
|
6405
|
+
...chartOptions.axis.y,
|
|
6406
|
+
...!horizontal && valueTicks ? { tickValues: valueTicks } : {}
|
|
6407
|
+
})] })
|
|
6408
|
+
}),
|
|
6004
6409
|
withTooltips && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccessibleTooltip, {
|
|
6005
6410
|
tooltipPlacement,
|
|
6006
6411
|
tooltipAnchorTop,
|
|
6412
|
+
style: tooltipStyle,
|
|
6007
6413
|
detectBounds: true,
|
|
6008
6414
|
snapTooltipToDatumX: true,
|
|
6009
6415
|
snapTooltipToDatumY: true,
|