@automattic/charts 1.7.0 → 1.8.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 +8 -0
- package/dist/index.cjs +358 -51
- 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 +358 -51
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/charts/bar-chart/bar-chart.tsx +196 -42
- package/src/charts/bar-chart/private/comparison-bars-geometry.ts +70 -0
- package/src/charts/bar-chart/private/comparison-bars.tsx +154 -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 +46 -5
- package/src/charts/bar-chart/test/bar-chart.test.tsx +191 -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
|
@@ -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(...allValues), Math.max(...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,143 @@ 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
|
+
children: rects
|
|
4653
|
+
});
|
|
4654
|
+
};
|
|
4655
|
+
//#endregion
|
|
4456
4656
|
//#region src/charts/bar-chart/bar-chart.tsx
|
|
4457
4657
|
const validateData$2 = (data) => {
|
|
4458
4658
|
if (!data?.length) return "No data available";
|
|
@@ -4481,13 +4681,14 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4481
4681
|
}, [height]);
|
|
4482
4682
|
const [selectedIndex, setSelectedIndex] = useState(void 0);
|
|
4483
4683
|
const [isNavigating, setIsNavigating] = useState(false);
|
|
4684
|
+
const primarySeriesForNav = dataWithVisibleZeros.filter((s) => s.options?.type !== "comparison");
|
|
4484
4685
|
const { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation({
|
|
4485
4686
|
selectedIndex,
|
|
4486
4687
|
setSelectedIndex,
|
|
4487
4688
|
isNavigating,
|
|
4488
4689
|
setIsNavigating,
|
|
4489
4690
|
chartRef,
|
|
4490
|
-
totalPoints: Math.max(0, ...
|
|
4691
|
+
totalPoints: Math.max(0, ...primarySeriesForNav.map((s) => s.data?.length || 0)) * primarySeriesForNav.length
|
|
4491
4692
|
});
|
|
4492
4693
|
const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
|
|
4493
4694
|
const seriesWithVisibility = useMemo(() => {
|
|
@@ -4510,6 +4711,69 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4510
4711
|
const allSeriesHidden = useMemo(() => {
|
|
4511
4712
|
return seriesWithVisibility.every(({ isVisible }) => !isVisible);
|
|
4512
4713
|
}, [seriesWithVisibility]);
|
|
4714
|
+
const primaryEntries = useMemo(() => seriesWithVisibility.filter(({ isVisible, series }) => isVisible && series.options?.type !== "comparison"), [seriesWithVisibility]);
|
|
4715
|
+
const primaryKeys = useMemo(() => primaryEntries.map(({ series }) => series.label), [primaryEntries]);
|
|
4716
|
+
const comparisonEntries = useMemo(() => {
|
|
4717
|
+
const primaryByGroup = new Map(primaryEntries.map(({ series, index }) => [series.group, {
|
|
4718
|
+
label: series.label,
|
|
4719
|
+
index
|
|
4720
|
+
}]));
|
|
4721
|
+
const entries = [];
|
|
4722
|
+
seriesWithVisibility.forEach(({ series, index, isVisible }) => {
|
|
4723
|
+
if (!isVisible || series.options?.type !== "comparison") return;
|
|
4724
|
+
const primary = primaryByGroup.get(series.group) ?? (primaryEntries.length === 1 ? {
|
|
4725
|
+
label: primaryEntries[0].series.label,
|
|
4726
|
+
index: primaryEntries[0].index
|
|
4727
|
+
} : void 0);
|
|
4728
|
+
if (!primary || !primaryKeys.includes(primary.label)) return;
|
|
4729
|
+
entries.push({
|
|
4730
|
+
series,
|
|
4731
|
+
index,
|
|
4732
|
+
primaryKey: primary.label,
|
|
4733
|
+
primaryIndex: primary.index
|
|
4734
|
+
});
|
|
4735
|
+
});
|
|
4736
|
+
return entries;
|
|
4737
|
+
}, [
|
|
4738
|
+
seriesWithVisibility,
|
|
4739
|
+
primaryEntries,
|
|
4740
|
+
primaryKeys
|
|
4741
|
+
]);
|
|
4742
|
+
const comparisonWidthFactor = useMemo(() => {
|
|
4743
|
+
if (comparisonEntries.length === 0) return void 0;
|
|
4744
|
+
return getElementStyles({
|
|
4745
|
+
data: comparisonEntries[0].series,
|
|
4746
|
+
index: comparisonEntries[0].index
|
|
4747
|
+
}).barStyles?.widthFactor ?? 1.5;
|
|
4748
|
+
}, [comparisonEntries, getElementStyles]);
|
|
4749
|
+
const groupPadding = useMemo(() => {
|
|
4750
|
+
const basePadding = chartOptions.barGroup.padding;
|
|
4751
|
+
if (!comparisonWidthFactor || comparisonWidthFactor <= 1) return basePadding;
|
|
4752
|
+
const p = 1 - (1 - COMPARISON_INNER_GAP) / comparisonWidthFactor;
|
|
4753
|
+
return Math.min(Math.max(p, basePadding), MAX_GROUP_PADDING);
|
|
4754
|
+
}, [chartOptions.barGroup.padding, comparisonWidthFactor]);
|
|
4755
|
+
const { xScale, yScale } = useMemo(() => {
|
|
4756
|
+
if (comparisonEntries.length === 0) return {
|
|
4757
|
+
xScale: chartOptions.xScale,
|
|
4758
|
+
yScale: chartOptions.yScale
|
|
4759
|
+
};
|
|
4760
|
+
const tighten = (scale) => ({
|
|
4761
|
+
...scale,
|
|
4762
|
+
paddingInner: (scale.paddingInner ?? .1) * COMPARISON_TICK_GAP_FACTOR
|
|
4763
|
+
});
|
|
4764
|
+
return horizontal ? {
|
|
4765
|
+
xScale: chartOptions.xScale,
|
|
4766
|
+
yScale: tighten(chartOptions.yScale)
|
|
4767
|
+
} : {
|
|
4768
|
+
xScale: tighten(chartOptions.xScale),
|
|
4769
|
+
yScale: chartOptions.yScale
|
|
4770
|
+
};
|
|
4771
|
+
}, [
|
|
4772
|
+
comparisonEntries.length,
|
|
4773
|
+
chartOptions.xScale,
|
|
4774
|
+
chartOptions.yScale,
|
|
4775
|
+
horizontal
|
|
4776
|
+
]);
|
|
4513
4777
|
const getBarBackground = useCallback((index) => () => withPatterns ? `url(#${getPatternId(chartId, index)})` : getElementStyles({
|
|
4514
4778
|
data: dataSorted[index],
|
|
4515
4779
|
index
|
|
@@ -4519,26 +4783,70 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4519
4783
|
dataSorted,
|
|
4520
4784
|
chartId
|
|
4521
4785
|
]);
|
|
4786
|
+
const resolveComparisonFill = useCallback((entry) => withPatterns ? `url(#${getPatternId(chartId, entry.primaryIndex)})` : getElementStyles({
|
|
4787
|
+
data: entry.series,
|
|
4788
|
+
index: entry.index
|
|
4789
|
+
}).color, [
|
|
4790
|
+
withPatterns,
|
|
4791
|
+
chartId,
|
|
4792
|
+
getElementStyles
|
|
4793
|
+
]);
|
|
4522
4794
|
const renderDefaultTooltip = useCallback(({ tooltipData }) => {
|
|
4523
4795
|
const nearestDatum = tooltipData?.nearestDatum?.datum;
|
|
4524
4796
|
if (!nearestDatum) return null;
|
|
4797
|
+
const primaryKey = tooltipData?.nearestDatum?.key;
|
|
4798
|
+
const categoryLabel = chartOptions.tooltip.labelFormatter(nearestDatum.label || (nearestDatum.date ? nearestDatum.date.getTime() : 0), 0, []);
|
|
4799
|
+
const comparisonEntry = comparisonEntries.find((entry) => entry.primaryKey === primaryKey);
|
|
4800
|
+
const comparisonDatum = comparisonEntry?.series.data.find((point) => {
|
|
4801
|
+
const p = point;
|
|
4802
|
+
return nearestDatum.label != null ? p.label === nearestDatum.label : !!nearestDatum.date && !!p.date && p.date.getTime() === nearestDatum.date.getTime();
|
|
4803
|
+
});
|
|
4804
|
+
if (comparisonEntry && comparisonDatum && comparisonDatum.value != null) return /* @__PURE__ */ jsxs("div", {
|
|
4805
|
+
className: bar_chart_module_default["bar-chart__tooltip"],
|
|
4806
|
+
children: [
|
|
4807
|
+
/* @__PURE__ */ jsx("div", {
|
|
4808
|
+
className: bar_chart_module_default["bar-chart__tooltip-header"],
|
|
4809
|
+
children: categoryLabel
|
|
4810
|
+
}),
|
|
4811
|
+
/* @__PURE__ */ jsxs("div", {
|
|
4812
|
+
className: bar_chart_module_default["bar-chart__tooltip-row"],
|
|
4813
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
4814
|
+
className: bar_chart_module_default["bar-chart__tooltip-label"],
|
|
4815
|
+
children: [primaryKey, ":"]
|
|
4816
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
4817
|
+
className: bar_chart_module_default["bar-chart__tooltip-value"],
|
|
4818
|
+
children: formatNumber(nearestDatum.value)
|
|
4819
|
+
})]
|
|
4820
|
+
}),
|
|
4821
|
+
/* @__PURE__ */ jsxs("div", {
|
|
4822
|
+
className: bar_chart_module_default["bar-chart__tooltip-row"],
|
|
4823
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
4824
|
+
className: bar_chart_module_default["bar-chart__tooltip-label"],
|
|
4825
|
+
children: [comparisonEntry.series.label, ":"]
|
|
4826
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
4827
|
+
className: bar_chart_module_default["bar-chart__tooltip-value"],
|
|
4828
|
+
children: formatNumber(comparisonDatum.value)
|
|
4829
|
+
})]
|
|
4830
|
+
})
|
|
4831
|
+
]
|
|
4832
|
+
});
|
|
4525
4833
|
return /* @__PURE__ */ jsxs("div", {
|
|
4526
4834
|
className: bar_chart_module_default["bar-chart__tooltip"],
|
|
4527
4835
|
children: [/* @__PURE__ */ jsx("div", {
|
|
4528
4836
|
className: bar_chart_module_default["bar-chart__tooltip-header"],
|
|
4529
|
-
children:
|
|
4837
|
+
children: primaryKey
|
|
4530
4838
|
}), /* @__PURE__ */ jsxs("div", {
|
|
4531
4839
|
className: bar_chart_module_default["bar-chart__tooltip-row"],
|
|
4532
4840
|
children: [/* @__PURE__ */ jsxs("span", {
|
|
4533
4841
|
className: bar_chart_module_default["bar-chart__tooltip-label"],
|
|
4534
|
-
children: [
|
|
4842
|
+
children: [categoryLabel, ":"]
|
|
4535
4843
|
}), /* @__PURE__ */ jsx("span", {
|
|
4536
4844
|
className: bar_chart_module_default["bar-chart__tooltip-value"],
|
|
4537
4845
|
children: formatNumber(nearestDatum.value)
|
|
4538
4846
|
})]
|
|
4539
4847
|
})]
|
|
4540
4848
|
});
|
|
4541
|
-
}, [chartOptions.tooltip]);
|
|
4849
|
+
}, [chartOptions.tooltip, comparisonEntries]);
|
|
4542
4850
|
const renderPattern = useCallback((index, color) => {
|
|
4543
4851
|
const patternType = index % 4;
|
|
4544
4852
|
const id = getPatternId(chartId, index);
|
|
@@ -4575,8 +4883,10 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4575
4883
|
}
|
|
4576
4884
|
}, [chartId]);
|
|
4577
4885
|
const createPatternBorderStyle = useCallback((index, color) => {
|
|
4886
|
+
const patternId = getPatternId(chartId, index);
|
|
4578
4887
|
return `
|
|
4579
|
-
.visx-bar[fill="url(#${
|
|
4888
|
+
.visx-bar[fill="url(#${patternId})"],
|
|
4889
|
+
.bar-chart__comparison-bars rect[fill="url(#${patternId})"] {
|
|
4580
4890
|
stroke: ${color};
|
|
4581
4891
|
stroke-width: 1;
|
|
4582
4892
|
}
|
|
@@ -4584,11 +4894,13 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4584
4894
|
}, [chartId]);
|
|
4585
4895
|
const createKeyboardHighlightStyle = useCallback(() => {
|
|
4586
4896
|
if (selectedIndex === void 0) return "";
|
|
4587
|
-
const
|
|
4588
|
-
const
|
|
4589
|
-
const
|
|
4590
|
-
|
|
4591
|
-
if (dataPointIndex >=
|
|
4897
|
+
const primaryCount = primaryEntries.length;
|
|
4898
|
+
const maxDataPoints = Math.max(...primaryEntries.map((e) => e.series.data.length));
|
|
4899
|
+
const dataPointIndex = Math.floor(selectedIndex / primaryCount);
|
|
4900
|
+
const seriesIndex = selectedIndex % primaryCount;
|
|
4901
|
+
if (dataPointIndex >= maxDataPoints || seriesIndex >= primaryCount) return "";
|
|
4902
|
+
const seriesData = primaryEntries[seriesIndex]?.series;
|
|
4903
|
+
if (!seriesData || dataPointIndex >= seriesData.data.length) return "";
|
|
4592
4904
|
return `
|
|
4593
4905
|
.bar-chart[data-chart-id="bar-chart-${chartId}"] .visx-bar-group .visx-bar:nth-child(${seriesIndex * maxDataPoints + dataPointIndex + 1}) {
|
|
4594
4906
|
stroke: #005fcc;
|
|
@@ -4597,7 +4909,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4597
4909
|
`;
|
|
4598
4910
|
}, [
|
|
4599
4911
|
selectedIndex,
|
|
4600
|
-
|
|
4912
|
+
primaryEntries,
|
|
4601
4913
|
chartId
|
|
4602
4914
|
]);
|
|
4603
4915
|
const error = validateData$2(dataSorted);
|
|
@@ -4670,8 +4982,8 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4670
4982
|
...defaultMargin,
|
|
4671
4983
|
...margin
|
|
4672
4984
|
},
|
|
4673
|
-
xScale
|
|
4674
|
-
yScale
|
|
4985
|
+
xScale,
|
|
4986
|
+
yScale,
|
|
4675
4987
|
horizontal,
|
|
4676
4988
|
pointerEventsDataKey: "nearest",
|
|
4677
4989
|
children: [
|
|
@@ -4695,18 +5007,25 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
4695
5007
|
height: chartHeight,
|
|
4696
5008
|
children: __("All series are hidden. Click legend items to show data.", "jetpack-charts")
|
|
4697
5009
|
}) : null,
|
|
5010
|
+
/* @__PURE__ */ jsx(ComparisonBars, {
|
|
5011
|
+
comparisonEntries,
|
|
5012
|
+
primaryKeys,
|
|
5013
|
+
groupPadding,
|
|
5014
|
+
horizontal,
|
|
5015
|
+
xAccessor: chartOptions.accessors.xAccessor,
|
|
5016
|
+
yAccessor: chartOptions.accessors.yAccessor,
|
|
5017
|
+
getElementStyles,
|
|
5018
|
+
resolveFill: resolveComparisonFill
|
|
5019
|
+
}),
|
|
4698
5020
|
/* @__PURE__ */ jsx(BarGroup, {
|
|
4699
|
-
padding:
|
|
4700
|
-
children:
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
colorAccessor: getBarBackground(index)
|
|
4708
|
-
}, seriesData?.label);
|
|
4709
|
-
})
|
|
5021
|
+
padding: groupPadding,
|
|
5022
|
+
children: primaryEntries.map(({ series: seriesData, index }) => /* @__PURE__ */ jsx(BarSeries, {
|
|
5023
|
+
dataKey: seriesData?.label,
|
|
5024
|
+
data: seriesData.data,
|
|
5025
|
+
yAccessor: chartOptions.accessors.yAccessor,
|
|
5026
|
+
xAccessor: chartOptions.accessors.xAccessor,
|
|
5027
|
+
colorAccessor: getBarBackground(index)
|
|
5028
|
+
}, seriesData?.label))
|
|
4710
5029
|
}),
|
|
4711
5030
|
/* @__PURE__ */ jsx(Axis, { ...chartOptions.axis.x }),
|
|
4712
5031
|
/* @__PURE__ */ jsx(Axis, { ...chartOptions.axis.y }),
|
|
@@ -5319,9 +5638,7 @@ const DEFAULT_BACKGROUND_COLOR = "#ffffff";
|
|
|
5319
5638
|
*/
|
|
5320
5639
|
const GeoChartInternal = ({ className, data, width, height, region = "world", resolution = "countries", renderPlaceholder }) => {
|
|
5321
5640
|
const { getElementStyles, theme: { geoChart: { featureFillColor }, backgroundColor } } = useGlobalChartsContext();
|
|
5322
|
-
const loadingPlaceholder = /* @__PURE__ */ jsx(
|
|
5323
|
-
align: "center",
|
|
5324
|
-
justify: "center",
|
|
5641
|
+
const loadingPlaceholder = /* @__PURE__ */ jsx(Center, {
|
|
5325
5642
|
className: clsx("geo-chart", geo_chart_module_default.container, className),
|
|
5326
5643
|
style: {
|
|
5327
5644
|
width,
|
|
@@ -5379,9 +5696,7 @@ const GeoChartInternal = ({ className, data, width, height, region = "world", re
|
|
|
5379
5696
|
defaultFillColorHex,
|
|
5380
5697
|
sanitizedData.hasHtmlTooltips
|
|
5381
5698
|
]);
|
|
5382
|
-
return /* @__PURE__ */ jsx(
|
|
5383
|
-
align: "center",
|
|
5384
|
-
justify: "center",
|
|
5699
|
+
return /* @__PURE__ */ jsx(Center, {
|
|
5385
5700
|
className: clsx("geo-chart", geo_chart_module_default.container, className),
|
|
5386
5701
|
style: {
|
|
5387
5702
|
width,
|
|
@@ -8053,7 +8368,6 @@ function RadialWipeAnimation({ id, radius, innerRadius = 0, durationMs = 1e3, wi
|
|
|
8053
8368
|
//#region src/charts/pie-chart/pie-chart.module.scss
|
|
8054
8369
|
var pie_chart_module_default = {
|
|
8055
8370
|
"pie-chart": "a8ccharts-gnszbG-pie-chart",
|
|
8056
|
-
"pie-chart__centering": "a8ccharts-gnszbG-pie-chart__centering",
|
|
8057
8371
|
"pie-chart--responsive": "a8ccharts-gnszbG-pie-chart--responsive"
|
|
8058
8372
|
};
|
|
8059
8373
|
//#endregion
|
|
@@ -8218,11 +8532,8 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
|
|
|
8218
8532
|
const innerRadius = thickness === 0 ? 0 : outerRadius * (1 - thickness);
|
|
8219
8533
|
const maxCornerRadius = (outerRadius - innerRadius) / 2;
|
|
8220
8534
|
const cornerRadius = cornerScale ? Math.min(cornerScale * outerRadius, maxCornerRadius) : 0;
|
|
8221
|
-
return /* @__PURE__ */ jsx(
|
|
8535
|
+
return /* @__PURE__ */ jsx(Center, {
|
|
8222
8536
|
ref: containerRef,
|
|
8223
|
-
align: "center",
|
|
8224
|
-
justify: "center",
|
|
8225
|
-
className: pie_chart_module_default["pie-chart__centering"],
|
|
8226
8537
|
children: /* @__PURE__ */ jsxs("svg", {
|
|
8227
8538
|
viewBox: `0 0 ${width} ${height}`,
|
|
8228
8539
|
preserveAspectRatio: "xMidYMid meet",
|
|
@@ -8335,7 +8646,6 @@ var pie_semi_circle_chart_module_default = {
|
|
|
8335
8646
|
"label": "a8ccharts-YtTOxW-label",
|
|
8336
8647
|
"note": "a8ccharts-YtTOxW-note",
|
|
8337
8648
|
"pie-semi-circle-chart": "a8ccharts-YtTOxW-pie-semi-circle-chart",
|
|
8338
|
-
"pie-semi-circle-chart__centering": "a8ccharts-YtTOxW-pie-semi-circle-chart__centering",
|
|
8339
8649
|
"pie-semi-circle-chart--responsive": "a8ccharts-YtTOxW-pie-semi-circle-chart--responsive"
|
|
8340
8650
|
};
|
|
8341
8651
|
//#endregion
|
|
@@ -8515,11 +8825,8 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
|
|
|
8515
8825
|
const height = width / 2;
|
|
8516
8826
|
const radius = height;
|
|
8517
8827
|
const innerRadius = radius * (1 - thickness);
|
|
8518
|
-
return /* @__PURE__ */ jsx(
|
|
8828
|
+
return /* @__PURE__ */ jsx(Center, {
|
|
8519
8829
|
ref: containerRef,
|
|
8520
|
-
align: "center",
|
|
8521
|
-
justify: "center",
|
|
8522
|
-
className: pie_semi_circle_chart_module_default["pie-semi-circle-chart__centering"],
|
|
8523
8830
|
children: /* @__PURE__ */ jsxs("svg", {
|
|
8524
8831
|
width,
|
|
8525
8832
|
height,
|