@automattic/charts 1.7.0 → 1.8.1
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 +13 -0
- package/dist/index.cjs +347 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +6 -4
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +348 -62
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/charts/bar-chart/bar-chart.tsx +210 -49
- package/src/charts/bar-chart/private/comparison-bars-geometry.ts +70 -0
- package/src/charts/bar-chart/private/comparison-bars.tsx +155 -0
- package/src/charts/bar-chart/private/comparison-constants.ts +33 -0
- package/src/charts/bar-chart/private/index.ts +9 -0
- package/src/charts/bar-chart/private/test/comparison-bars-geometry.test.ts +47 -0
- package/src/charts/bar-chart/private/test/comparison-bars.test.tsx +183 -0
- package/src/charts/bar-chart/private/use-bar-chart-options.ts +49 -5
- package/src/charts/bar-chart/test/bar-chart.test.tsx +329 -1
- package/src/charts/geo-chart/geo-chart.tsx +5 -9
- package/src/charts/pie-chart/pie-chart.module.scss +0 -4
- package/src/charts/pie-chart/pie-chart.tsx +3 -8
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss +0 -5
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +3 -8
- package/src/charts/private/center/center.module.scss +4 -0
- package/src/charts/private/center/center.tsx +33 -0
- package/src/charts/private/center/index.ts +2 -0
- package/src/charts/private/center/test/center.test.tsx +60 -0
- package/src/charts/private/svg-empty-state/svg-empty-state.module.scss +0 -2
- package/src/charts/private/svg-empty-state/svg-empty-state.tsx +2 -4
- package/src/providers/chart-context/global-charts-provider.tsx +2 -0
- package/src/providers/chart-context/test/chart-context.test.tsx +13 -1
- package/src/providers/chart-context/themes.ts +8 -0
- package/src/providers/chart-context/types.ts +2 -0
- package/src/types.ts +16 -0
- package/src/utils/get-styles.ts +36 -13
- package/src/utils/index.ts +6 -1
- package/src/utils/test/get-styles.test.ts +47 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getStringWidth as getStringWidth$1 } from "./visx/text/index.js";
|
|
2
2
|
import { formatNumber, formatNumberCompact } from "@automattic/number-formatters";
|
|
3
3
|
import { AnimatedAreaSeries, AnimatedAreaStack, AreaSeries, Axis, BarGroup, BarSeries, DataContext, Grid, Tooltip, TooltipContext, XYChart, buildChartTheme } from "@visx/xychart";
|
|
4
|
-
import { __ } from "@wordpress/i18n";
|
|
4
|
+
import { __, sprintf } from "@wordpress/i18n";
|
|
5
5
|
import clsx from "clsx";
|
|
6
6
|
import * as React from "react";
|
|
7
7
|
import { Children, Fragment, Fragment as Fragment$2, createContext, createContext as createContext$1, createElement, forwardRef, forwardRef as forwardRef$1, forwardRef as forwardRef$2, isValidElement, memo, useCallback, useCallback as useCallback$1, useContext, useContext as useContext$1, useEffect, useEffect as useEffect$1, useId, useImperativeHandle, useLayoutEffect, useMemo, useMemo as useMemo$1, useRef, useRef as useRef$1, useState, useState as useState$1 } from "react";
|
|
@@ -262,6 +262,20 @@ function getSeriesLineStyles(seriesData, index, providerTheme) {
|
|
|
262
262
|
return seriesData.options?.seriesLineStyle ?? themeSemanticLineStyle ?? themeSeriesLineStyle ?? {};
|
|
263
263
|
}
|
|
264
264
|
/**
|
|
265
|
+
* Utility to get consolidated bar styles for a series by semantic type.
|
|
266
|
+
* Mirrors getSeriesLineStyles: a series with `options.type` (e.g. 'comparison')
|
|
267
|
+
* resolves to `theme.barChart.barStyles[ type ]`.
|
|
268
|
+
*
|
|
269
|
+
* @param {SeriesData} seriesData - The series data containing styling options
|
|
270
|
+
* @param {number} index - The index of the series in the data array
|
|
271
|
+
* @param {ChartTheme} providerTheme - The chart theme configuration
|
|
272
|
+
* @return {BarStyles} The consolidated bar styles for the series
|
|
273
|
+
*/
|
|
274
|
+
function getSeriesBarStyles(seriesData, index, providerTheme) {
|
|
275
|
+
const type = seriesData.options?.type;
|
|
276
|
+
return (type && providerTheme?.barChart?.barStyles?.[type]) ?? {};
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
265
279
|
* Utility function to get shape styles for a legend item
|
|
266
280
|
*
|
|
267
281
|
* @param {SeriesData} series - The series data containing styling options
|
|
@@ -273,13 +287,17 @@ function getSeriesLineStyles(seriesData, index, providerTheme) {
|
|
|
273
287
|
function getItemShapeStyles(series, index, theme, legendShape) {
|
|
274
288
|
const seriesShapeStyles = series.options?.legendShapeStyle ?? {};
|
|
275
289
|
const lineStyles = legendShape === "line" ? getSeriesLineStyles(series, index, theme) : {};
|
|
290
|
+
const barOpacity = legendShape !== "line" ? getSeriesBarStyles(series, index, theme).opacity : void 0;
|
|
291
|
+
const barShapeStyles = barOpacity !== void 0 ? { opacity: barOpacity } : {};
|
|
276
292
|
const themeShapeStyles = theme.legend?.shapeStyles?.[index];
|
|
277
|
-
const
|
|
293
|
+
const explicitStyles = {
|
|
278
294
|
...seriesShapeStyles,
|
|
279
295
|
...lineStyles
|
|
280
296
|
};
|
|
281
|
-
|
|
282
|
-
|
|
297
|
+
return {
|
|
298
|
+
...Object.values(explicitStyles).some((value) => value !== void 0 && value !== null && value !== "") ? explicitStyles : themeShapeStyles ?? {},
|
|
299
|
+
...barShapeStyles
|
|
300
|
+
};
|
|
283
301
|
}
|
|
284
302
|
//#endregion
|
|
285
303
|
//#region src/utils/is-safari.ts
|
|
@@ -725,6 +743,10 @@ const defaultTheme = {
|
|
|
725
743
|
strokeDasharray: "4 4",
|
|
726
744
|
strokeLinecap: "square"
|
|
727
745
|
} } },
|
|
746
|
+
barChart: { barStyles: { comparison: {
|
|
747
|
+
widthFactor: 1.5,
|
|
748
|
+
opacity: .5
|
|
749
|
+
} } },
|
|
728
750
|
sparkline: {
|
|
729
751
|
margin: {
|
|
730
752
|
top: 2,
|
|
@@ -831,6 +853,7 @@ const GlobalChartsProvider = ({ children, theme }) => {
|
|
|
831
853
|
overrideColor: overrideColor || isSeriesData && data?.options?.stroke || isPointPercentageData && data?.color
|
|
832
854
|
}),
|
|
833
855
|
lineStyles: isSeriesData ? getSeriesLineStyles(data, index, providerTheme) : {},
|
|
856
|
+
barStyles: isSeriesData ? getSeriesBarStyles(data, index, providerTheme) : {},
|
|
834
857
|
glyph: providerTheme.glyphs?.[index],
|
|
835
858
|
shapeStyles: isSeriesData ? getItemShapeStyles(data, index, providerTheme, legendShape) : {}
|
|
836
859
|
};
|
|
@@ -2635,6 +2658,32 @@ const DefaultGlyph = (props) => {
|
|
|
2635
2658
|
});
|
|
2636
2659
|
};
|
|
2637
2660
|
//#endregion
|
|
2661
|
+
//#region src/charts/private/center/center.module.scss
|
|
2662
|
+
var center_module_default = { "center": "a8ccharts-w3qxlG-center" };
|
|
2663
|
+
//#endregion
|
|
2664
|
+
//#region src/charts/private/center/center.tsx
|
|
2665
|
+
/**
|
|
2666
|
+
* Centers its children on both axes and fills its parent.
|
|
2667
|
+
*
|
|
2668
|
+
* A thin wrapper around `Stack` with `align="center"` and `justify="center"`
|
|
2669
|
+
* defaults (both overridable) plus `width: 100%; height: 100%`. Reads more
|
|
2670
|
+
* honestly than a `Stack` with both axes centered, and lets call sites drop
|
|
2671
|
+
* ad-hoc `*__centering` classes. Forwards its ref and spreads remaining props
|
|
2672
|
+
* onto the underlying `Stack`.
|
|
2673
|
+
*
|
|
2674
|
+
* @param props - Stack props; `align`/`justify` default to `"center"`.
|
|
2675
|
+
* @param ref - Forwarded to the underlying element.
|
|
2676
|
+
* @return The centered layout element.
|
|
2677
|
+
*/
|
|
2678
|
+
const Center = forwardRef(({ align = "center", justify = "center", className, ...props }, ref) => /* @__PURE__ */ jsx(Stack, {
|
|
2679
|
+
ref,
|
|
2680
|
+
align,
|
|
2681
|
+
justify,
|
|
2682
|
+
className: clsx(center_module_default.center, className),
|
|
2683
|
+
...props
|
|
2684
|
+
}));
|
|
2685
|
+
Center.displayName = "Center";
|
|
2686
|
+
//#endregion
|
|
2638
2687
|
//#region src/charts/private/svg-empty-state/svg-empty-state.module.scss
|
|
2639
2688
|
var svg_empty_state_module_default = { "svg-empty-state": "a8ccharts-udGPVq-svg-empty-state" };
|
|
2640
2689
|
//#endregion
|
|
@@ -2659,9 +2708,7 @@ const SvgEmptyState = ({ x, y, width, height, children }) => {
|
|
|
2659
2708
|
y: y - height / 2,
|
|
2660
2709
|
width,
|
|
2661
2710
|
height,
|
|
2662
|
-
children: /* @__PURE__ */ jsx(
|
|
2663
|
-
align: "center",
|
|
2664
|
-
justify: "center",
|
|
2711
|
+
children: /* @__PURE__ */ jsx(Center, {
|
|
2665
2712
|
className: svg_empty_state_module_default["svg-empty-state"],
|
|
2666
2713
|
children
|
|
2667
2714
|
})
|
|
@@ -4341,6 +4388,10 @@ const TruncatedXTickComponent = createTruncatedTickComponent("x");
|
|
|
4341
4388
|
const TruncatedYTickComponent = createTruncatedTickComponent("y");
|
|
4342
4389
|
//#endregion
|
|
4343
4390
|
//#region src/charts/bar-chart/private/use-bar-chart-options.ts
|
|
4391
|
+
/** Outer padding of the category band scale (space at the chart edges). */
|
|
4392
|
+
const BASE_BAND_PADDING = .2;
|
|
4393
|
+
/** Inner padding of the category band scale (the base gap between ticks). */
|
|
4394
|
+
const BASE_BAND_PADDING_INNER = .1;
|
|
4344
4395
|
const formatDateTick = (timestamp) => {
|
|
4345
4396
|
return new Date(timestamp).toLocaleDateString(void 0, {
|
|
4346
4397
|
month: "short",
|
|
@@ -4368,8 +4419,8 @@ function useBarChartOptions(data, horizontal, options = {}) {
|
|
|
4368
4419
|
const defaultOptions = useMemo(() => {
|
|
4369
4420
|
const bandScale = {
|
|
4370
4421
|
type: "band",
|
|
4371
|
-
padding:
|
|
4372
|
-
paddingInner:
|
|
4422
|
+
padding: BASE_BAND_PADDING,
|
|
4423
|
+
paddingInner: BASE_BAND_PADDING_INNER
|
|
4373
4424
|
};
|
|
4374
4425
|
const linearScale = {
|
|
4375
4426
|
type: "linear",
|
|
@@ -4408,13 +4459,29 @@ function useBarChartOptions(data, horizontal, options = {}) {
|
|
|
4408
4459
|
}, [data]);
|
|
4409
4460
|
return useMemo(() => {
|
|
4410
4461
|
const { xTickFormat, yTickFormat, tooltipLabelFormatter: defaultTooltipLabelFormatter, xAccessor, yAccessor, gridVisibility, xScale: baseXScale, yScale: baseYScale } = defaultOptions[horizontal ? "horizontal" : "vertical"];
|
|
4462
|
+
let valueScaleDomainOverride = {};
|
|
4463
|
+
if (data.some((s) => s.options?.type === "comparison")) {
|
|
4464
|
+
if (!(!horizontal ? options.yScale?.domain : options.xScale?.domain)) {
|
|
4465
|
+
const allValues = [];
|
|
4466
|
+
data.forEach((series) => {
|
|
4467
|
+
series.data.forEach((d) => {
|
|
4468
|
+
const enhanced = d;
|
|
4469
|
+
const v = enhanced.visualValue !== void 0 ? enhanced.visualValue : d.value;
|
|
4470
|
+
if (typeof v === "number" && Number.isFinite(v)) allValues.push(v);
|
|
4471
|
+
});
|
|
4472
|
+
});
|
|
4473
|
+
if (allValues.length > 0) valueScaleDomainOverride = { domain: [Math.min(0, ...allValues), Math.max(0, ...allValues)] };
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4411
4476
|
const xScale = {
|
|
4412
4477
|
...baseXScale,
|
|
4413
|
-
...options.xScale || {}
|
|
4478
|
+
...options.xScale || {},
|
|
4479
|
+
...horizontal ? valueScaleDomainOverride : {}
|
|
4414
4480
|
};
|
|
4415
4481
|
const yScale = {
|
|
4416
4482
|
...baseYScale,
|
|
4417
|
-
...options.yScale || {}
|
|
4483
|
+
...options.yScale || {},
|
|
4484
|
+
...!horizontal ? valueScaleDomainOverride : {}
|
|
4418
4485
|
};
|
|
4419
4486
|
const providedToolTipLabelFormatter = horizontal ? options.axis?.y?.tickFormat : options.axis?.x?.tickFormat;
|
|
4420
4487
|
const { labelOverflow: xLabelOverflow, ...xAxisOptions } = options.axis?.x || {};
|
|
@@ -4449,10 +4516,144 @@ function useBarChartOptions(data, horizontal, options = {}) {
|
|
|
4449
4516
|
}, [
|
|
4450
4517
|
defaultOptions,
|
|
4451
4518
|
options,
|
|
4452
|
-
horizontal
|
|
4519
|
+
horizontal,
|
|
4520
|
+
data
|
|
4453
4521
|
]);
|
|
4454
4522
|
}
|
|
4455
4523
|
//#endregion
|
|
4524
|
+
//#region src/charts/bar-chart/private/comparison-bars-geometry.ts
|
|
4525
|
+
/**
|
|
4526
|
+
* Output position of a value scale's baseline: zero if in-domain, else the
|
|
4527
|
+
* nearest range edge. Mirrors visx's getScaleBaseline so comparison shadows
|
|
4528
|
+
* sit on the same baseline as primary bars.
|
|
4529
|
+
*
|
|
4530
|
+
* @param {ValueScale} scale - The continuous value scale.
|
|
4531
|
+
* @return {number} The baseline output position in pixels.
|
|
4532
|
+
*/
|
|
4533
|
+
function getValueScaleBaseline(scale) {
|
|
4534
|
+
const [a, b] = scale.range().map((r) => Number(r) || 0);
|
|
4535
|
+
const isDescending = b < a;
|
|
4536
|
+
const maybeZero = scale(0);
|
|
4537
|
+
const [minOutput, maxOutput] = isDescending ? [b, a] : [a, b];
|
|
4538
|
+
if (isDescending) return Number.isFinite(maybeZero) ? Math.min(Math.max(minOutput, maybeZero), maxOutput) : maxOutput;
|
|
4539
|
+
return Number.isFinite(maybeZero) ? Math.min(Math.max(maybeZero, minOutput), maxOutput) : minOutput;
|
|
4540
|
+
}
|
|
4541
|
+
/**
|
|
4542
|
+
* Compute the rect for a comparison "shadow" bar, centered on the paired
|
|
4543
|
+
* primary bar slot and scaled by `widthFactor`.
|
|
4544
|
+
*
|
|
4545
|
+
* @param {object} params - Geometry inputs.
|
|
4546
|
+
* @param {boolean} params.horizontal - True for a horizontal bar chart, false for vertical.
|
|
4547
|
+
* @param {number} params.bandPosition - bandScale(category): start px of the category band.
|
|
4548
|
+
* @param {number} params.slotOffset - groupScale(primaryKey): offset of the primary slot within the band.
|
|
4549
|
+
* @param {number} params.slotThickness - groupScale.bandwidth(): primary bar thickness in px.
|
|
4550
|
+
* @param {number} params.valuePosition - valueScale(value): output px for the bar's data value.
|
|
4551
|
+
* @param {number} params.baseline - getValueScaleBaseline(valueScale): zero-line output px.
|
|
4552
|
+
* @param {number} params.widthFactor - Shadow thickness multiplier, e.g. 1.5 for 150% width.
|
|
4553
|
+
* @return {ComparisonRect} The {x, y, width, height} of the shadow rect.
|
|
4554
|
+
*/
|
|
4555
|
+
function computeComparisonRect(params) {
|
|
4556
|
+
const { horizontal, bandPosition, slotOffset, slotThickness, valuePosition, baseline, widthFactor } = params;
|
|
4557
|
+
const slotStart = bandPosition + slotOffset;
|
|
4558
|
+
const shadowThickness = slotThickness * widthFactor;
|
|
4559
|
+
const shadowStart = slotStart + slotThickness / 2 - shadowThickness / 2;
|
|
4560
|
+
const valueStart = Math.min(valuePosition, baseline);
|
|
4561
|
+
const valueLength = Math.abs(baseline - valuePosition);
|
|
4562
|
+
if (horizontal) return {
|
|
4563
|
+
x: valueStart,
|
|
4564
|
+
y: shadowStart,
|
|
4565
|
+
width: valueLength,
|
|
4566
|
+
height: shadowThickness
|
|
4567
|
+
};
|
|
4568
|
+
return {
|
|
4569
|
+
x: shadowStart,
|
|
4570
|
+
y: valueStart,
|
|
4571
|
+
width: shadowThickness,
|
|
4572
|
+
height: valueLength
|
|
4573
|
+
};
|
|
4574
|
+
}
|
|
4575
|
+
/**
|
|
4576
|
+
* Fraction of each per-series step left as a gap between bars within a single tick.
|
|
4577
|
+
* Larger = more space between adjacent series; the shadow spans `1 - COMPARISON_INNER_GAP` of the step.
|
|
4578
|
+
*/
|
|
4579
|
+
const COMPARISON_INNER_GAP = .1;
|
|
4580
|
+
/**
|
|
4581
|
+
* Upper clamp on the computed group padding, so bars can never collapse to zero width
|
|
4582
|
+
* even at very large `widthFactor` values.
|
|
4583
|
+
*/
|
|
4584
|
+
const MAX_GROUP_PADDING = .9;
|
|
4585
|
+
/**
|
|
4586
|
+
* Factor applied to the category band's `paddingInner` in comparison mode to tighten the
|
|
4587
|
+
* gap between ticks. `0.75` = a 25% reduction of the tick-gap padding.
|
|
4588
|
+
*/
|
|
4589
|
+
const COMPARISON_TICK_GAP_FACTOR = .75;
|
|
4590
|
+
//#endregion
|
|
4591
|
+
//#region src/charts/bar-chart/private/comparison-bars.tsx
|
|
4592
|
+
const ComparisonBars = ({ comparisonEntries, primaryKeys, groupPadding, horizontal, xAccessor, yAccessor, getElementStyles, resolveFill }) => {
|
|
4593
|
+
const context = useContext(DataContext);
|
|
4594
|
+
const xScale = context?.xScale;
|
|
4595
|
+
const yScale = context?.yScale;
|
|
4596
|
+
if (!xScale || !yScale || primaryKeys.length === 0) return null;
|
|
4597
|
+
const bandScale = horizontal ? yScale : xScale;
|
|
4598
|
+
const valueScale = horizontal ? xScale : yScale;
|
|
4599
|
+
const bandwidth = bandScale.bandwidth ? bandScale.bandwidth() : 0;
|
|
4600
|
+
if (!bandwidth) return null;
|
|
4601
|
+
const groupScale = scaleBand({
|
|
4602
|
+
domain: primaryKeys,
|
|
4603
|
+
range: [0, bandwidth],
|
|
4604
|
+
padding: groupPadding
|
|
4605
|
+
});
|
|
4606
|
+
const slotThickness = groupScale.bandwidth();
|
|
4607
|
+
const baseline = getValueScaleBaseline(valueScale);
|
|
4608
|
+
const bandAccessor = horizontal ? yAccessor : xAccessor;
|
|
4609
|
+
const valueAccessor = horizontal ? xAccessor : yAccessor;
|
|
4610
|
+
const rects = [];
|
|
4611
|
+
comparisonEntries.forEach((entry) => {
|
|
4612
|
+
const { series, index, primaryKey } = entry;
|
|
4613
|
+
const slotOffset = groupScale(primaryKey);
|
|
4614
|
+
if (slotOffset == null || !Number.isFinite(slotOffset)) return;
|
|
4615
|
+
const { barStyles } = getElementStyles({
|
|
4616
|
+
data: series,
|
|
4617
|
+
index
|
|
4618
|
+
});
|
|
4619
|
+
const opacity = barStyles?.opacity ?? .5;
|
|
4620
|
+
const widthFactor = barStyles?.widthFactor ?? 1.5;
|
|
4621
|
+
const fill = resolveFill(entry);
|
|
4622
|
+
series.data.forEach((datum, i) => {
|
|
4623
|
+
const bandPosition = Number(bandScale(bandAccessor(datum)));
|
|
4624
|
+
const valuePosition = Number(valueScale(Number(valueAccessor(datum))));
|
|
4625
|
+
if (!Number.isFinite(bandPosition) || !Number.isFinite(valuePosition)) {
|
|
4626
|
+
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.`);
|
|
4627
|
+
return;
|
|
4628
|
+
}
|
|
4629
|
+
const rect = computeComparisonRect({
|
|
4630
|
+
horizontal,
|
|
4631
|
+
bandPosition,
|
|
4632
|
+
slotOffset,
|
|
4633
|
+
slotThickness,
|
|
4634
|
+
valuePosition,
|
|
4635
|
+
baseline,
|
|
4636
|
+
widthFactor
|
|
4637
|
+
});
|
|
4638
|
+
rects.push(/* @__PURE__ */ jsx("rect", {
|
|
4639
|
+
x: rect.x,
|
|
4640
|
+
y: rect.y,
|
|
4641
|
+
width: rect.width,
|
|
4642
|
+
height: rect.height,
|
|
4643
|
+
fill,
|
|
4644
|
+
opacity
|
|
4645
|
+
}, `${index}-${i}`));
|
|
4646
|
+
});
|
|
4647
|
+
});
|
|
4648
|
+
if (rects.length === 0) return null;
|
|
4649
|
+
return /* @__PURE__ */ jsx("g", {
|
|
4650
|
+
className: "bar-chart__comparison-bars",
|
|
4651
|
+
pointerEvents: "none",
|
|
4652
|
+
"aria-hidden": "true",
|
|
4653
|
+
children: rects
|
|
4654
|
+
});
|
|
4655
|
+
};
|
|
4656
|
+
//#endregion
|
|
4456
4657
|
//#region src/charts/bar-chart/bar-chart.tsx
|
|
4457
4658
|
const validateData$2 = (data) => {
|
|
4458
4659
|
if (!data?.length) return "No data available";
|
|
@@ -4460,6 +4661,10 @@ const validateData$2 = (data) => {
|
|
|
4460
4661
|
return null;
|
|
4461
4662
|
};
|
|
4462
4663
|
const getPatternId = (chartId, index) => `bar-pattern-${chartId}-${index}`;
|
|
4664
|
+
const renderTooltipRow = (label, value) => /* @__PURE__ */ jsx("div", {
|
|
4665
|
+
className: bar_chart_module_default["bar-chart__tooltip-row"],
|
|
4666
|
+
children: sprintf(__("%1$s: %2$s", "jetpack-charts"), label, value)
|
|
4667
|
+
});
|
|
4463
4668
|
const BarChartInternal = ({ data, chartId: providedChartId, width, height, className, margin, withTooltips = false, showLegend = false, legend = {}, gridVisibility: gridVisibilityProp, renderTooltip, options = {}, orientation = "vertical", withPatterns = false, showZeroValues = false, animation, children, gap = "md" }) => {
|
|
4464
4669
|
const legendInteractive = legend.interactive ?? false;
|
|
4465
4670
|
const horizontal = orientation === "horizontal";
|
|
@@ -4481,13 +4686,14 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4481
4686
|
}, [height]);
|
|
4482
4687
|
const [selectedIndex, setSelectedIndex] = useState(void 0);
|
|
4483
4688
|
const [isNavigating, setIsNavigating] = useState(false);
|
|
4689
|
+
const primarySeriesForNav = dataWithVisibleZeros.filter((s) => s.options?.type !== "comparison");
|
|
4484
4690
|
const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
|
|
4485
4691
|
selectedIndex,
|
|
4486
4692
|
setSelectedIndex,
|
|
4487
4693
|
isNavigating,
|
|
4488
4694
|
setIsNavigating,
|
|
4489
4695
|
chartRef,
|
|
4490
|
-
totalPoints: Math.max(0, ...
|
|
4696
|
+
totalPoints: Math.max(0, ...primarySeriesForNav.map((s) => s.data?.length || 0)) * primarySeriesForNav.length
|
|
4491
4697
|
});
|
|
4492
4698
|
const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
|
|
4493
4699
|
const seriesWithVisibility = useMemo(() => {
|
|
@@ -4510,6 +4716,70 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4510
4716
|
const allSeriesHidden = useMemo(() => {
|
|
4511
4717
|
return seriesWithVisibility.every(({ isVisible }) => !isVisible);
|
|
4512
4718
|
}, [seriesWithVisibility]);
|
|
4719
|
+
const primaryEntries = useMemo(() => seriesWithVisibility.filter(({ isVisible, series }) => isVisible && series.options?.type !== "comparison"), [seriesWithVisibility]);
|
|
4720
|
+
const primaryKeys = useMemo(() => primaryEntries.map(({ series }) => series.label), [primaryEntries]);
|
|
4721
|
+
const primarySeries = useMemo(() => primaryEntries.map(({ series }) => series), [primaryEntries]);
|
|
4722
|
+
const comparisonEntries = useMemo(() => {
|
|
4723
|
+
const primaryByGroup = new Map(primaryEntries.map(({ series, index }) => [series.group, {
|
|
4724
|
+
label: series.label,
|
|
4725
|
+
index
|
|
4726
|
+
}]));
|
|
4727
|
+
const entries = [];
|
|
4728
|
+
seriesWithVisibility.forEach(({ series, index, isVisible }) => {
|
|
4729
|
+
if (!isVisible || series.options?.type !== "comparison") return;
|
|
4730
|
+
const primary = primaryByGroup.get(series.group) ?? (primaryEntries.length === 1 ? {
|
|
4731
|
+
label: primaryEntries[0].series.label,
|
|
4732
|
+
index: primaryEntries[0].index
|
|
4733
|
+
} : void 0);
|
|
4734
|
+
if (!primary || !primaryKeys.includes(primary.label)) return;
|
|
4735
|
+
entries.push({
|
|
4736
|
+
series,
|
|
4737
|
+
index,
|
|
4738
|
+
primaryKey: primary.label,
|
|
4739
|
+
primaryIndex: primary.index
|
|
4740
|
+
});
|
|
4741
|
+
});
|
|
4742
|
+
return entries;
|
|
4743
|
+
}, [
|
|
4744
|
+
seriesWithVisibility,
|
|
4745
|
+
primaryEntries,
|
|
4746
|
+
primaryKeys
|
|
4747
|
+
]);
|
|
4748
|
+
const comparisonWidthFactor = useMemo(() => {
|
|
4749
|
+
if (comparisonEntries.length === 0) return void 0;
|
|
4750
|
+
return getElementStyles({
|
|
4751
|
+
data: comparisonEntries[0].series,
|
|
4752
|
+
index: comparisonEntries[0].index
|
|
4753
|
+
}).barStyles?.widthFactor ?? 1.5;
|
|
4754
|
+
}, [comparisonEntries, getElementStyles]);
|
|
4755
|
+
const groupPadding = useMemo(() => {
|
|
4756
|
+
const basePadding = chartOptions.barGroup.padding;
|
|
4757
|
+
if (!comparisonWidthFactor || comparisonWidthFactor <= 1) return basePadding;
|
|
4758
|
+
const p = 1 - (1 - COMPARISON_INNER_GAP) / comparisonWidthFactor;
|
|
4759
|
+
return Math.min(Math.max(p, basePadding), MAX_GROUP_PADDING);
|
|
4760
|
+
}, [chartOptions.barGroup.padding, comparisonWidthFactor]);
|
|
4761
|
+
const { xScale, yScale } = useMemo(() => {
|
|
4762
|
+
if (comparisonEntries.length === 0) return {
|
|
4763
|
+
xScale: chartOptions.xScale,
|
|
4764
|
+
yScale: chartOptions.yScale
|
|
4765
|
+
};
|
|
4766
|
+
const tighten = (scale) => ({
|
|
4767
|
+
...scale,
|
|
4768
|
+
paddingInner: (scale.paddingInner ?? .1) * COMPARISON_TICK_GAP_FACTOR
|
|
4769
|
+
});
|
|
4770
|
+
return horizontal ? {
|
|
4771
|
+
xScale: chartOptions.xScale,
|
|
4772
|
+
yScale: tighten(chartOptions.yScale)
|
|
4773
|
+
} : {
|
|
4774
|
+
xScale: tighten(chartOptions.xScale),
|
|
4775
|
+
yScale: chartOptions.yScale
|
|
4776
|
+
};
|
|
4777
|
+
}, [
|
|
4778
|
+
comparisonEntries.length,
|
|
4779
|
+
chartOptions.xScale,
|
|
4780
|
+
chartOptions.yScale,
|
|
4781
|
+
horizontal
|
|
4782
|
+
]);
|
|
4513
4783
|
const getBarBackground = useCallback((index) => () => withPatterns ? `url(#${getPatternId(chartId, index)})` : getElementStyles({
|
|
4514
4784
|
data: dataSorted[index],
|
|
4515
4785
|
index
|
|
@@ -4519,26 +4789,43 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4519
4789
|
dataSorted,
|
|
4520
4790
|
chartId
|
|
4521
4791
|
]);
|
|
4792
|
+
const resolveComparisonFill = useCallback((entry) => withPatterns ? `url(#${getPatternId(chartId, entry.primaryIndex)})` : getElementStyles({
|
|
4793
|
+
data: entry.series,
|
|
4794
|
+
index: entry.index
|
|
4795
|
+
}).color, [
|
|
4796
|
+
withPatterns,
|
|
4797
|
+
chartId,
|
|
4798
|
+
getElementStyles
|
|
4799
|
+
]);
|
|
4522
4800
|
const renderDefaultTooltip = useCallback(({ tooltipData }) => {
|
|
4523
4801
|
const nearestDatum = tooltipData?.nearestDatum?.datum;
|
|
4524
4802
|
if (!nearestDatum) return null;
|
|
4803
|
+
const primaryKey = tooltipData?.nearestDatum?.key;
|
|
4804
|
+
const categoryLabel = chartOptions.tooltip.labelFormatter(nearestDatum.label || (nearestDatum.date ? nearestDatum.date.getTime() : 0), 0, []);
|
|
4805
|
+
const comparisonEntry = comparisonEntries.find((entry) => entry.primaryKey === primaryKey);
|
|
4806
|
+
const comparisonDatum = comparisonEntry?.series.data.find((point) => {
|
|
4807
|
+
const p = point;
|
|
4808
|
+
return nearestDatum.label != null ? p.label === nearestDatum.label : !!nearestDatum.date && !!p.date && p.date.getTime() === nearestDatum.date.getTime();
|
|
4809
|
+
});
|
|
4810
|
+
if (comparisonEntry && comparisonDatum && comparisonDatum.value != null) return /* @__PURE__ */ jsxs("div", {
|
|
4811
|
+
className: bar_chart_module_default["bar-chart__tooltip"],
|
|
4812
|
+
children: [
|
|
4813
|
+
/* @__PURE__ */ jsx("div", {
|
|
4814
|
+
className: bar_chart_module_default["bar-chart__tooltip-header"],
|
|
4815
|
+
children: categoryLabel
|
|
4816
|
+
}),
|
|
4817
|
+
renderTooltipRow(primaryKey, formatNumber(nearestDatum.value)),
|
|
4818
|
+
renderTooltipRow(comparisonEntry.series.label, formatNumber(comparisonDatum.value))
|
|
4819
|
+
]
|
|
4820
|
+
});
|
|
4525
4821
|
return /* @__PURE__ */ jsxs("div", {
|
|
4526
4822
|
className: bar_chart_module_default["bar-chart__tooltip"],
|
|
4527
4823
|
children: [/* @__PURE__ */ jsx("div", {
|
|
4528
4824
|
className: bar_chart_module_default["bar-chart__tooltip-header"],
|
|
4529
|
-
children:
|
|
4530
|
-
}),
|
|
4531
|
-
className: bar_chart_module_default["bar-chart__tooltip-row"],
|
|
4532
|
-
children: [/* @__PURE__ */ jsxs("span", {
|
|
4533
|
-
className: bar_chart_module_default["bar-chart__tooltip-label"],
|
|
4534
|
-
children: [chartOptions.tooltip.labelFormatter(nearestDatum.label || (nearestDatum.date ? nearestDatum.date.getTime() : 0), 0, []), ":"]
|
|
4535
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
4536
|
-
className: bar_chart_module_default["bar-chart__tooltip-value"],
|
|
4537
|
-
children: formatNumber(nearestDatum.value)
|
|
4538
|
-
})]
|
|
4539
|
-
})]
|
|
4825
|
+
children: primaryKey
|
|
4826
|
+
}), renderTooltipRow(categoryLabel, formatNumber(nearestDatum.value))]
|
|
4540
4827
|
});
|
|
4541
|
-
}, [chartOptions.tooltip]);
|
|
4828
|
+
}, [chartOptions.tooltip, comparisonEntries]);
|
|
4542
4829
|
const renderPattern = useCallback((index, color) => {
|
|
4543
4830
|
const patternType = index % 4;
|
|
4544
4831
|
const id = getPatternId(chartId, index);
|
|
@@ -4575,8 +4862,10 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4575
4862
|
}
|
|
4576
4863
|
}, [chartId]);
|
|
4577
4864
|
const createPatternBorderStyle = useCallback((index, color) => {
|
|
4865
|
+
const patternId = getPatternId(chartId, index);
|
|
4578
4866
|
return `
|
|
4579
|
-
.visx-bar[fill="url(#${
|
|
4867
|
+
.visx-bar[fill="url(#${patternId})"],
|
|
4868
|
+
.bar-chart__comparison-bars rect[fill="url(#${patternId})"] {
|
|
4580
4869
|
stroke: ${color};
|
|
4581
4870
|
stroke-width: 1;
|
|
4582
4871
|
}
|
|
@@ -4584,11 +4873,13 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4584
4873
|
}, [chartId]);
|
|
4585
4874
|
const createKeyboardHighlightStyle = useCallback(() => {
|
|
4586
4875
|
if (selectedIndex === void 0) return "";
|
|
4587
|
-
const
|
|
4588
|
-
const
|
|
4589
|
-
const
|
|
4590
|
-
|
|
4591
|
-
if (dataPointIndex >=
|
|
4876
|
+
const primaryCount = primaryEntries.length;
|
|
4877
|
+
const maxDataPoints = Math.max(...primaryEntries.map((e) => e.series.data.length));
|
|
4878
|
+
const dataPointIndex = Math.floor(selectedIndex / primaryCount);
|
|
4879
|
+
const seriesIndex = selectedIndex % primaryCount;
|
|
4880
|
+
if (dataPointIndex >= maxDataPoints || seriesIndex >= primaryCount) return "";
|
|
4881
|
+
const seriesData = primaryEntries[seriesIndex]?.series;
|
|
4882
|
+
if (!seriesData || dataPointIndex >= seriesData.data.length) return "";
|
|
4592
4883
|
return `
|
|
4593
4884
|
.bar-chart[data-chart-id="bar-chart-${chartId}"] .visx-bar-group .visx-bar:nth-child(${seriesIndex * maxDataPoints + dataPointIndex + 1}) {
|
|
4594
4885
|
stroke: #005fcc;
|
|
@@ -4597,7 +4888,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4597
4888
|
`;
|
|
4598
4889
|
}, [
|
|
4599
4890
|
selectedIndex,
|
|
4600
|
-
|
|
4891
|
+
primaryEntries,
|
|
4601
4892
|
chartId
|
|
4602
4893
|
]);
|
|
4603
4894
|
const error = validateData$2(dataSorted);
|
|
@@ -4670,8 +4961,8 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4670
4961
|
...defaultMargin,
|
|
4671
4962
|
...margin
|
|
4672
4963
|
},
|
|
4673
|
-
xScale
|
|
4674
|
-
yScale
|
|
4964
|
+
xScale,
|
|
4965
|
+
yScale,
|
|
4675
4966
|
horizontal,
|
|
4676
4967
|
pointerEventsDataKey: "nearest",
|
|
4677
4968
|
children: [
|
|
@@ -4695,18 +4986,25 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4695
4986
|
height: chartHeight,
|
|
4696
4987
|
children: __("All series are hidden. Click legend items to show data.", "jetpack-charts")
|
|
4697
4988
|
}) : null,
|
|
4989
|
+
/* @__PURE__ */ jsx(ComparisonBars, {
|
|
4990
|
+
comparisonEntries,
|
|
4991
|
+
primaryKeys,
|
|
4992
|
+
groupPadding,
|
|
4993
|
+
horizontal,
|
|
4994
|
+
xAccessor: chartOptions.accessors.xAccessor,
|
|
4995
|
+
yAccessor: chartOptions.accessors.yAccessor,
|
|
4996
|
+
getElementStyles,
|
|
4997
|
+
resolveFill: resolveComparisonFill
|
|
4998
|
+
}),
|
|
4698
4999
|
/* @__PURE__ */ jsx(BarGroup, {
|
|
4699
|
-
padding:
|
|
4700
|
-
children:
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
colorAccessor: getBarBackground(index)
|
|
4708
|
-
}, seriesData?.label);
|
|
4709
|
-
})
|
|
5000
|
+
padding: groupPadding,
|
|
5001
|
+
children: primaryEntries.map(({ series: seriesData, index }) => /* @__PURE__ */ jsx(BarSeries, {
|
|
5002
|
+
dataKey: seriesData?.label,
|
|
5003
|
+
data: seriesData.data,
|
|
5004
|
+
yAccessor: chartOptions.accessors.yAccessor,
|
|
5005
|
+
xAccessor: chartOptions.accessors.xAccessor,
|
|
5006
|
+
colorAccessor: getBarBackground(index)
|
|
5007
|
+
}, seriesData?.label))
|
|
4710
5008
|
}),
|
|
4711
5009
|
/* @__PURE__ */ jsx(Axis, { ...chartOptions.axis.x }),
|
|
4712
5010
|
/* @__PURE__ */ jsx(Axis, { ...chartOptions.axis.y }),
|
|
@@ -4718,7 +5016,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4718
5016
|
selectedIndex,
|
|
4719
5017
|
tooltipRef,
|
|
4720
5018
|
keyboardFocusedClassName: bar_chart_module_default["bar-chart__tooltip--keyboard-focused"],
|
|
4721
|
-
series:
|
|
5019
|
+
series: primarySeries,
|
|
4722
5020
|
mode: "individual"
|
|
4723
5021
|
})
|
|
4724
5022
|
]
|
|
@@ -5319,9 +5617,7 @@ const DEFAULT_BACKGROUND_COLOR = "#ffffff";
|
|
|
5319
5617
|
*/
|
|
5320
5618
|
const GeoChartInternal = ({ className, data, width, height, region = "world", resolution = "countries", renderPlaceholder }) => {
|
|
5321
5619
|
const { getElementStyles, theme: { geoChart: { featureFillColor }, backgroundColor } } = useGlobalChartsContext();
|
|
5322
|
-
const loadingPlaceholder = /* @__PURE__ */ jsx(
|
|
5323
|
-
align: "center",
|
|
5324
|
-
justify: "center",
|
|
5620
|
+
const loadingPlaceholder = /* @__PURE__ */ jsx(Center, {
|
|
5325
5621
|
className: clsx("geo-chart", geo_chart_module_default.container, className),
|
|
5326
5622
|
style: {
|
|
5327
5623
|
width,
|
|
@@ -5379,9 +5675,7 @@ const GeoChartInternal = ({ className, data, width, height, region = "world", re
|
|
|
5379
5675
|
defaultFillColorHex,
|
|
5380
5676
|
sanitizedData.hasHtmlTooltips
|
|
5381
5677
|
]);
|
|
5382
|
-
return /* @__PURE__ */ jsx(
|
|
5383
|
-
align: "center",
|
|
5384
|
-
justify: "center",
|
|
5678
|
+
return /* @__PURE__ */ jsx(Center, {
|
|
5385
5679
|
className: clsx("geo-chart", geo_chart_module_default.container, className),
|
|
5386
5680
|
style: {
|
|
5387
5681
|
width,
|
|
@@ -8053,7 +8347,6 @@ function RadialWipeAnimation({ id, radius, innerRadius = 0, durationMs = 1e3, wi
|
|
|
8053
8347
|
//#region src/charts/pie-chart/pie-chart.module.scss
|
|
8054
8348
|
var pie_chart_module_default = {
|
|
8055
8349
|
"pie-chart": "a8ccharts-gnszbG-pie-chart",
|
|
8056
|
-
"pie-chart__centering": "a8ccharts-gnszbG-pie-chart__centering",
|
|
8057
8350
|
"pie-chart--responsive": "a8ccharts-gnszbG-pie-chart--responsive"
|
|
8058
8351
|
};
|
|
8059
8352
|
//#endregion
|
|
@@ -8218,11 +8511,8 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
|
|
|
8218
8511
|
const innerRadius = thickness === 0 ? 0 : outerRadius * (1 - thickness);
|
|
8219
8512
|
const maxCornerRadius = (outerRadius - innerRadius) / 2;
|
|
8220
8513
|
const cornerRadius = cornerScale ? Math.min(cornerScale * outerRadius, maxCornerRadius) : 0;
|
|
8221
|
-
return /* @__PURE__ */ jsx(
|
|
8514
|
+
return /* @__PURE__ */ jsx(Center, {
|
|
8222
8515
|
ref: containerRef,
|
|
8223
|
-
align: "center",
|
|
8224
|
-
justify: "center",
|
|
8225
|
-
className: pie_chart_module_default["pie-chart__centering"],
|
|
8226
8516
|
children: /* @__PURE__ */ jsxs("svg", {
|
|
8227
8517
|
viewBox: `0 0 ${width} ${height}`,
|
|
8228
8518
|
preserveAspectRatio: "xMidYMid meet",
|
|
@@ -8335,7 +8625,6 @@ var pie_semi_circle_chart_module_default = {
|
|
|
8335
8625
|
"label": "a8ccharts-YtTOxW-label",
|
|
8336
8626
|
"note": "a8ccharts-YtTOxW-note",
|
|
8337
8627
|
"pie-semi-circle-chart": "a8ccharts-YtTOxW-pie-semi-circle-chart",
|
|
8338
|
-
"pie-semi-circle-chart__centering": "a8ccharts-YtTOxW-pie-semi-circle-chart__centering",
|
|
8339
8628
|
"pie-semi-circle-chart--responsive": "a8ccharts-YtTOxW-pie-semi-circle-chart--responsive"
|
|
8340
8629
|
};
|
|
8341
8630
|
//#endregion
|
|
@@ -8515,11 +8804,8 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
|
|
|
8515
8804
|
const height = width / 2;
|
|
8516
8805
|
const radius = height;
|
|
8517
8806
|
const innerRadius = radius * (1 - thickness);
|
|
8518
|
-
return /* @__PURE__ */ jsx(
|
|
8807
|
+
return /* @__PURE__ */ jsx(Center, {
|
|
8519
8808
|
ref: containerRef,
|
|
8520
|
-
align: "center",
|
|
8521
|
-
justify: "center",
|
|
8522
|
-
className: pie_semi_circle_chart_module_default["pie-semi-circle-chart__centering"],
|
|
8523
8809
|
children: /* @__PURE__ */ jsxs("svg", {
|
|
8524
8810
|
width,
|
|
8525
8811
|
height,
|