@automattic/charts 1.10.3 → 1.11.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 +11 -0
- package/TOKENS.md +72 -0
- package/dist/index.cjs +143 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +54 -41
- package/dist/index.d.cts +17 -7
- package/dist/index.d.ts +17 -7
- package/dist/index.js +143 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss +7 -7
- package/src/charts/heatmap-chart/heatmap-chart.module.scss +18 -13
- package/src/charts/heatmap-chart/heatmap-chart.tsx +11 -7
- package/src/charts/heatmap-chart/private/heatmap-legend.tsx +3 -3
- package/src/charts/heatmap-chart/test/heatmap-chart.test.tsx +13 -7
- package/src/charts/leaderboard-chart/hooks/index.ts +1 -0
- package/src/charts/leaderboard-chart/hooks/use-fitted-row-count.ts +115 -0
- package/src/charts/leaderboard-chart/leaderboard-chart.module.scss +39 -12
- package/src/charts/leaderboard-chart/leaderboard-chart.tsx +67 -14
- package/src/charts/leaderboard-chart/test/leaderboard-chart-fit-rows.test.tsx +365 -0
- package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +73 -2
- package/src/charts/leaderboard-chart/types.ts +12 -0
- package/src/charts/line-chart/line-chart.module.scss +2 -2
- package/src/charts/private/grid-control/grid-control.module.scss +1 -1
- package/src/charts/private/svg-empty-state/svg-empty-state.module.scss +1 -1
- package/src/charts/private/x-zoom.module.scss +12 -10
- package/src/components/tooltip/base-tooltip.module.scss +3 -2
- package/src/components/trend-indicator/trend-indicator.module.scss +6 -5
- package/src/providers/chart-context/themes.ts +33 -19
- package/src/types.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.11.0] - 2026-07-27
|
|
9
|
+
### Added
|
|
10
|
+
- Charts: add a `fitRows` prop to LeaderboardChart so a chart in a fixed-height container shows only the rows that fit instead of scrolling. [#50688]
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Charts: standardise package CSS custom properties on the --a8c-charts-{category}-{name} naming convention and document the token catalog; existing public override variables keep working via deprecated aliases. [#50656]
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Leaderboard: Distinguish unavailable percentage changes from missing comparison data. [#50690]
|
|
17
|
+
|
|
8
18
|
## [1.10.3] - 2026-07-22
|
|
9
19
|
### Fixed
|
|
10
20
|
- LeaderboardChart: Keep row spacing and column alignment identical whether or not a row is interactive. [#50657]
|
|
@@ -935,6 +945,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
935
945
|
- Fixed lints following ESLint rule changes for TS [#40584]
|
|
936
946
|
- Fixing a bug in Chart storybook data. [#40640]
|
|
937
947
|
|
|
948
|
+
[1.11.0]: https://github.com/Automattic/charts/compare/v1.10.3...v1.11.0
|
|
938
949
|
[1.10.3]: https://github.com/Automattic/charts/compare/v1.10.2...v1.10.3
|
|
939
950
|
[1.10.2]: https://github.com/Automattic/charts/compare/v1.10.1...v1.10.2
|
|
940
951
|
[1.10.1]: https://github.com/Automattic/charts/compare/v1.10.0...v1.10.1
|
package/TOKENS.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Charts CSS custom-property catalog
|
|
2
|
+
|
|
3
|
+
`@automattic/charts` exposes its themeable values as CSS custom properties under a
|
|
4
|
+
single convention: `--a8c-charts-{category}-{name}`. `{category}` mirrors the WPDS
|
|
5
|
+
token type (`color`, `dimension`, `border-radius`).
|
|
6
|
+
|
|
7
|
+
## How it resolves today
|
|
8
|
+
|
|
9
|
+
Nothing emits these variables at runtime yet (that is CHARTS-203). Each reference in
|
|
10
|
+
the library carries its default inline as a fallback:
|
|
11
|
+
|
|
12
|
+
```scss
|
|
13
|
+
stroke: var(--a8c-charts-color-grid, var(--wpds-color-stroke-surface-neutral, #dbdbdb));
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
So an `--a8c-charts-*` variable you set on a chart container wins; otherwise the
|
|
17
|
+
mapped `--wpds-*` token wins; otherwise the spec-value hex wins. Setting any
|
|
18
|
+
`--a8c-charts-*` variable on a chart ancestor overrides that role.
|
|
19
|
+
|
|
20
|
+
## Tier 1 — semantic catalog (shared, themeable roles)
|
|
21
|
+
|
|
22
|
+
| Role | Maps to `--wpds-*` | Fallback |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `--a8c-charts-color-grid` | `--wpds-color-stroke-surface-neutral` | `#dbdbdb` |
|
|
25
|
+
| `--a8c-charts-color-axis` | `--wpds-color-stroke-surface-neutral` | `#dbdbdb` |
|
|
26
|
+
| `--a8c-charts-color-tick` | `--wpds-color-stroke-surface-neutral` | `#dbdbdb` |
|
|
27
|
+
| `--a8c-charts-color-label` | `--wpds-color-foreground-content-neutral` | `#1e1e1e` |
|
|
28
|
+
| `--a8c-charts-color-label-secondary` | `--wpds-color-foreground-content-neutral-weak` | `#707070` |
|
|
29
|
+
| `--a8c-charts-color-label-inverse` | `--wpds-color-foreground-interactive-neutral-strong` | `#f0f0f0` |
|
|
30
|
+
| `--a8c-charts-color-label-on-fill` | _(none — white-on-series-fill, no WPDS fit)_ | `#FFFFFF` |
|
|
31
|
+
| `--a8c-charts-color-annotation` | `--wpds-color-foreground-content-neutral` | `#1e1e1e` |
|
|
32
|
+
| `--a8c-charts-color-trend-up` | `--wpds-color-foreground-content-success-weak` | `#008030` |
|
|
33
|
+
| `--a8c-charts-color-trend-down` | `--wpds-color-foreground-content-error-weak` | `#cc1818` |
|
|
34
|
+
| `--a8c-charts-color-trend-neutral` | `--wpds-color-foreground-content-neutral-weak` | `#707070` |
|
|
35
|
+
| `--a8c-charts-color-background` | `--wpds-color-background-surface-neutral-strong` | `#fff` |
|
|
36
|
+
| `--a8c-charts-color-surface-secondary` | `--wpds-color-background-surface-neutral-weak` | `#f4f4f4` |
|
|
37
|
+
| `--a8c-charts-color-track` | `--wpds-color-background-track-neutral-weak` | `#f0f0f0` |
|
|
38
|
+
| `--a8c-charts-color-tooltip-surface` | _(none — translucent dark surface, no WPDS fit)_ | `rgba(0,0,0,0.85)` |
|
|
39
|
+
| `--a8c-charts-color-focus` | `--wpds-color-stroke-focus` | `#3858e9` |
|
|
40
|
+
|
|
41
|
+
`--a8c-charts-color-axis` (axis line) and `--a8c-charts-color-tick` (tick marks)
|
|
42
|
+
resolve to the same value as `--a8c-charts-color-grid` by default. They are kept as
|
|
43
|
+
distinct roles so axis, tick marks, and gridlines can be themed independently; each
|
|
44
|
+
maps directly to its own `--wpds-*` token rather than chaining through `grid`.
|
|
45
|
+
|
|
46
|
+
## Tier 2 — component-specific variables
|
|
47
|
+
|
|
48
|
+
Instance styling knobs, on the same convention but not shared semantic roles:
|
|
49
|
+
`--a8c-charts-border-radius-leaderboard-bar`,
|
|
50
|
+
`--a8c-charts-dimension-leaderboard-bar-hover-inset`,
|
|
51
|
+
`--a8c-charts-color-heatmap-*`, `--a8c-charts-dimension-heatmap-*`,
|
|
52
|
+
`--a8c-charts-heatmap-cell-intensity`.
|
|
53
|
+
|
|
54
|
+
`--a8c-charts-heatmap-cell-intensity` is the one variable without a `{category}`
|
|
55
|
+
segment: it holds a unitless 0–1 scalar consumed inside `color-mix()`, not a colour.
|
|
56
|
+
|
|
57
|
+
The heatmap Tier-2 variables (`--a8c-charts-color-heatmap-*`,
|
|
58
|
+
`--a8c-charts-dimension-heatmap-*`, `--a8c-charts-heatmap-cell-intensity`) are
|
|
59
|
+
**component-emitted** — the chart sets them from JS per render — not consumer
|
|
60
|
+
override points.
|
|
61
|
+
|
|
62
|
+
## Public override variables
|
|
63
|
+
|
|
64
|
+
These are documented, supported override points. Deprecated names still work — they
|
|
65
|
+
are read as an inner fallback behind the new name:
|
|
66
|
+
|
|
67
|
+
| New | Deprecated alias |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `--a8c-charts-color-trend-up` | `--charts-trend-up-color` |
|
|
70
|
+
| `--a8c-charts-color-trend-down` | `--charts-trend-down-color` |
|
|
71
|
+
| `--a8c-charts-color-trend-neutral` | `--charts-trend-neutral-color` |
|
|
72
|
+
| `--a8c-charts-border-radius-leaderboard-bar` | `--a8c--charts--leaderboard--bar--border-radius` |
|
package/dist/index.cjs
CHANGED
|
@@ -725,9 +725,9 @@ const getChartColor = (index, colorCache) => {
|
|
|
725
725
|
* Default theme configuration
|
|
726
726
|
*/
|
|
727
727
|
const defaultTheme = {
|
|
728
|
-
backgroundColor: "var(--wpds-color-background-surface-neutral-strong, #fff)",
|
|
728
|
+
backgroundColor: "var(--a8c-charts-color-background, var(--wpds-color-background-surface-neutral-strong, #fff))",
|
|
729
729
|
labelBackgroundColor: "transparent",
|
|
730
|
-
labelTextColor: "#FFFFFF",
|
|
730
|
+
labelTextColor: "var(--a8c-charts-color-label-on-fill, #FFFFFF)",
|
|
731
731
|
colors: [
|
|
732
732
|
"#98C8DF",
|
|
733
733
|
"#006DAB",
|
|
@@ -736,59 +736,59 @@ const defaultTheme = {
|
|
|
736
736
|
"#FF8C8F"
|
|
737
737
|
],
|
|
738
738
|
gridStyles: {
|
|
739
|
-
stroke: "var(--wpds-color-stroke-surface-neutral, #dbdbdb)",
|
|
739
|
+
stroke: "var(--a8c-charts-color-grid, var(--wpds-color-stroke-surface-neutral, #dbdbdb))",
|
|
740
740
|
strokeWidth: 1
|
|
741
741
|
},
|
|
742
742
|
tickLength: 4,
|
|
743
743
|
gridColor: "",
|
|
744
744
|
gridColorDark: "",
|
|
745
745
|
xTickLineStyles: {
|
|
746
|
-
stroke: "var(--wpds-color-stroke-surface-neutral, #dbdbdb)",
|
|
746
|
+
stroke: "var(--a8c-charts-color-tick, var(--wpds-color-stroke-surface-neutral, #dbdbdb))",
|
|
747
747
|
strokeWidth: 1
|
|
748
748
|
},
|
|
749
749
|
xAxisLineStyles: {
|
|
750
|
-
stroke: "var(--wpds-color-stroke-surface-neutral, #dbdbdb)",
|
|
750
|
+
stroke: "var(--a8c-charts-color-axis, var(--wpds-color-stroke-surface-neutral, #dbdbdb))",
|
|
751
751
|
strokeWidth: 1
|
|
752
752
|
},
|
|
753
753
|
legend: {
|
|
754
|
-
labelStyles: { color: "var(--wpds-color-foreground-content-neutral, #1e1e1e)" },
|
|
754
|
+
labelStyles: { color: "var(--a8c-charts-color-label, var(--wpds-color-foreground-content-neutral, #1e1e1e))" },
|
|
755
755
|
containerStyles: {},
|
|
756
756
|
shapeStyles: []
|
|
757
757
|
},
|
|
758
758
|
seriesLineStyles: [],
|
|
759
759
|
glyphs: [],
|
|
760
760
|
svgLabelSmall: {
|
|
761
|
-
fill: "var(--wpds-color-foreground-content-neutral, #1e1e1e)",
|
|
761
|
+
fill: "var(--a8c-charts-color-label, var(--wpds-color-foreground-content-neutral, #1e1e1e))",
|
|
762
762
|
fontFamily: "inherit"
|
|
763
763
|
},
|
|
764
764
|
svgLabelBig: { fontFamily: "inherit" },
|
|
765
765
|
annotationStyles: {
|
|
766
766
|
label: {
|
|
767
|
-
anchorLineStroke: "var(--wpds-color-foreground-content-neutral, #1e1e1e)",
|
|
768
|
-
backgroundFill: "var(--wpds-color-background-surface-neutral-strong, #fff)"
|
|
767
|
+
anchorLineStroke: "var(--a8c-charts-color-annotation, var(--wpds-color-foreground-content-neutral, #1e1e1e))",
|
|
768
|
+
backgroundFill: "var(--a8c-charts-color-background, var(--wpds-color-background-surface-neutral-strong, #fff))"
|
|
769
769
|
},
|
|
770
|
-
connector: { stroke: "var(--wpds-color-foreground-content-neutral, #1e1e1e)" },
|
|
770
|
+
connector: { stroke: "var(--a8c-charts-color-annotation, var(--wpds-color-foreground-content-neutral, #1e1e1e))" },
|
|
771
771
|
circleSubject: {
|
|
772
772
|
stroke: "transparent",
|
|
773
|
-
fill: "var(--wpds-color-foreground-content-neutral, #1e1e1e)",
|
|
773
|
+
fill: "var(--a8c-charts-color-annotation, var(--wpds-color-foreground-content-neutral, #1e1e1e))",
|
|
774
774
|
radius: 5
|
|
775
775
|
}
|
|
776
776
|
},
|
|
777
|
-
geoChart: { featureFillColor: "var(--wpds-color-background-surface-neutral-weak, #f4f4f4)" },
|
|
777
|
+
geoChart: { featureFillColor: "var(--a8c-charts-color-surface-secondary, var(--wpds-color-background-surface-neutral-weak, #f4f4f4))" },
|
|
778
778
|
leaderboardChart: {
|
|
779
779
|
rowGap: 12,
|
|
780
780
|
columnGap: 4,
|
|
781
781
|
labelSpacing: "xs",
|
|
782
782
|
deltaColors: [
|
|
783
|
-
"var(--wpds-color-foreground-content-error-weak, #cc1818)",
|
|
784
|
-
"var(--wpds-color-foreground-content-neutral-weak, #707070)",
|
|
785
|
-
"var(--wpds-color-foreground-content-success-weak, #008030)"
|
|
783
|
+
"var(--a8c-charts-color-trend-down, var(--wpds-color-foreground-content-error-weak, #cc1818))",
|
|
784
|
+
"var(--a8c-charts-color-trend-neutral, var(--wpds-color-foreground-content-neutral-weak, #707070))",
|
|
785
|
+
"var(--a8c-charts-color-trend-up, var(--wpds-color-foreground-content-success-weak, #008030))"
|
|
786
786
|
]
|
|
787
787
|
},
|
|
788
788
|
conversionFunnelChart: {
|
|
789
|
-
backgroundColor: "var(--wpds-color-background-surface-neutral-weak, #f4f4f4)",
|
|
790
|
-
positiveChangeColor: "var(--wpds-color-foreground-content-success-weak, #008030)",
|
|
791
|
-
negativeChangeColor: "var(--wpds-color-foreground-content-error-weak, #cc1818)"
|
|
789
|
+
backgroundColor: "var(--a8c-charts-color-surface-secondary, var(--wpds-color-background-surface-neutral-weak, #f4f4f4))",
|
|
790
|
+
positiveChangeColor: "var(--a8c-charts-color-trend-up, var(--wpds-color-foreground-content-success-weak, #008030))",
|
|
791
|
+
negativeChangeColor: "var(--a8c-charts-color-trend-down, var(--wpds-color-foreground-content-error-weak, #cc1818))"
|
|
792
792
|
},
|
|
793
793
|
lineChart: { lineStyles: { comparison: {
|
|
794
794
|
strokeDasharray: "4 4",
|
|
@@ -6427,9 +6427,9 @@ const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
|
|
|
6427
6427
|
"aria-hidden": "true",
|
|
6428
6428
|
className: heatmap_chart_module_default["heatmap-chart__legend-swatch"],
|
|
6429
6429
|
style: {
|
|
6430
|
-
"--heatmap-primary": primaryColorHex,
|
|
6431
|
-
"--heatmap-
|
|
6432
|
-
"--intensity": intensity
|
|
6430
|
+
"--a8c-charts-color-heatmap-primary": primaryColorHex,
|
|
6431
|
+
"--a8c-charts-color-heatmap-background": backgroundColor,
|
|
6432
|
+
"--a8c-charts-heatmap-cell-intensity": intensity
|
|
6433
6433
|
}
|
|
6434
6434
|
}, index);
|
|
6435
6435
|
})
|
|
@@ -6587,17 +6587,17 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
|
|
|
6587
6587
|
children: (0, _wordpress_i18n.__)("No data available", "jetpack-charts")
|
|
6588
6588
|
})
|
|
6589
6589
|
});
|
|
6590
|
-
const columnTrack = compact ? "var(--heatmap-cell-size)" : `minmax(${minCellWidth ?? 0}px, ${maxCellWidth ? `${maxCellWidth}px` : "1fr"})`;
|
|
6591
|
-
const rowTrack = compact ? "var(--heatmap-cell-size)" : `minmax(${minCellHeight ?? 0}px, ${maxCellHeight ? `${maxCellHeight}px` : "1fr"})`;
|
|
6590
|
+
const columnTrack = compact ? "var(--a8c-charts-dimension-heatmap-cell-size)" : `minmax(${minCellWidth ?? 0}px, ${maxCellWidth ? `${maxCellWidth}px` : "1fr"})`;
|
|
6591
|
+
const rowTrack = compact ? "var(--a8c-charts-dimension-heatmap-cell-size)" : `minmax(${minCellHeight ?? 0}px, ${maxCellHeight ? `${maxCellHeight}px` : "1fr"})`;
|
|
6592
6592
|
const gridStyle = {
|
|
6593
|
-
"--heatmap-primary": primaryColorHex,
|
|
6594
|
-
"--heatmap-
|
|
6593
|
+
"--a8c-charts-color-heatmap-primary": primaryColorHex,
|
|
6594
|
+
"--a8c-charts-color-heatmap-background": theme.backgroundColor,
|
|
6595
6595
|
gridTemplateColumns: `auto repeat(${columns}, ${columnTrack})`,
|
|
6596
6596
|
gridTemplateRows: `auto repeat(${rows}, ${rowTrack})`
|
|
6597
6597
|
};
|
|
6598
6598
|
if (compact) {
|
|
6599
|
-
gridStyle["--heatmap-cell-gap"] = `${compactCellGap}px`;
|
|
6600
|
-
gridStyle["--heatmap-cell-size"] = `${compactCellSize}px`;
|
|
6599
|
+
gridStyle["--a8c-charts-dimension-heatmap-cell-gap"] = `${compactCellGap}px`;
|
|
6600
|
+
gridStyle["--a8c-charts-dimension-heatmap-cell-size"] = `${compactCellSize}px`;
|
|
6601
6601
|
}
|
|
6602
6602
|
const activeDescendant = selectedIndex !== void 0 ? `${chartId}-cell-${Math.floor(selectedIndex / rows)}-${selectedIndex % rows}` : void 0;
|
|
6603
6603
|
const heightCapped = !compact && Boolean(maxCellHeight);
|
|
@@ -6674,7 +6674,7 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
|
|
|
6674
6674
|
[heatmap_chart_module_default["heatmap-chart__cell--strong"]]: present && cellHasLightText(normalized),
|
|
6675
6675
|
[heatmap_chart_module_default["heatmap-chart__cell--selected"]]: selectedIndex === flatIndex
|
|
6676
6676
|
}),
|
|
6677
|
-
style: present ? { "--intensity": normalized } : void 0,
|
|
6677
|
+
style: present ? { "--a8c-charts-heatmap-cell-intensity": normalized } : void 0,
|
|
6678
6678
|
onMouseMove: handleCellMouseMove,
|
|
6679
6679
|
onMouseLeave: handleCellMouseLeave,
|
|
6680
6680
|
children: drawValues && present && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -9007,6 +9007,90 @@ function UnconnectedGrid(props, forwardedRef) {
|
|
|
9007
9007
|
}
|
|
9008
9008
|
var component_default = contextConnect(UnconnectedGrid, "Grid");
|
|
9009
9009
|
//#endregion
|
|
9010
|
+
//#region src/charts/leaderboard-chart/hooks/use-fitted-row-count.ts
|
|
9011
|
+
/**
|
|
9012
|
+
* Fractional layout rounding can put a row's bottom a hair past the container's
|
|
9013
|
+
* without any visible clipping. Allow that much slack before hiding a row.
|
|
9014
|
+
*
|
|
9015
|
+
* Exported so tests and stories assert against the real tolerance rather than
|
|
9016
|
+
* restating it.
|
|
9017
|
+
*/
|
|
9018
|
+
const SUBPIXEL_TOLERANCE = .5;
|
|
9019
|
+
/**
|
|
9020
|
+
* Counts how many leading rows fit inside the content container.
|
|
9021
|
+
*
|
|
9022
|
+
* Rows are read from the DOM rather than derived from constants: row height
|
|
9023
|
+
* depends on the theme's row gap, label wrapping, and whatever a caller renders
|
|
9024
|
+
* as a label, so any restated number would drift.
|
|
9025
|
+
*
|
|
9026
|
+
* Every row is a direct grid child marked with `data-row-index`; interactive rows
|
|
9027
|
+
* use a button wrapper and non-interactive rows use a div wrapper.
|
|
9028
|
+
*
|
|
9029
|
+
* @param enabled - Whether to measure at all. When false every row fits.
|
|
9030
|
+
* @param rowCount - Total number of rows rendered.
|
|
9031
|
+
* @param data - Rendered row data, whose geometry may change without changing the count.
|
|
9032
|
+
* @return Ref for the content container and the number of leading rows that fit.
|
|
9033
|
+
*/
|
|
9034
|
+
function useFittedRowCount(enabled, rowCount, data) {
|
|
9035
|
+
const contentRef = (0, react$1.useRef)(null);
|
|
9036
|
+
const [fittedCount, setFittedCount] = (0, react$1.useState)(rowCount);
|
|
9037
|
+
const [isMeasurable, setIsMeasurable] = (0, react$1.useState)(true);
|
|
9038
|
+
const measure = (0, react$1.useCallback)(() => {
|
|
9039
|
+
const content = contentRef.current;
|
|
9040
|
+
if (!content) return;
|
|
9041
|
+
content.scrollTop = 0;
|
|
9042
|
+
const rows = content.querySelectorAll(":scope > [data-leaderboard-grid] > [data-row-index]");
|
|
9043
|
+
const rowBottoms = [];
|
|
9044
|
+
rows.forEach((row) => {
|
|
9045
|
+
const index = Number(row.getAttribute("data-row-index"));
|
|
9046
|
+
if (!Number.isInteger(index) || index < 0 || index >= rowCount) return;
|
|
9047
|
+
const { bottom } = row.getBoundingClientRect();
|
|
9048
|
+
rowBottoms[index] = Math.max(rowBottoms[index] ?? -Infinity, bottom);
|
|
9049
|
+
});
|
|
9050
|
+
if (rowBottoms.length === 0) {
|
|
9051
|
+
setIsMeasurable(false);
|
|
9052
|
+
setFittedCount(rowCount);
|
|
9053
|
+
return;
|
|
9054
|
+
}
|
|
9055
|
+
setIsMeasurable(true);
|
|
9056
|
+
const contentBottom = content.getBoundingClientRect().bottom + SUBPIXEL_TOLERANCE;
|
|
9057
|
+
let fits = 0;
|
|
9058
|
+
while (fits < rowBottoms.length && rowBottoms[fits] <= contentBottom) fits++;
|
|
9059
|
+
setFittedCount((current) => current === fits ? current : fits);
|
|
9060
|
+
}, [rowCount]);
|
|
9061
|
+
(0, react$1.useLayoutEffect)(() => {
|
|
9062
|
+
if (!enabled) {
|
|
9063
|
+
setFittedCount(rowCount);
|
|
9064
|
+
return;
|
|
9065
|
+
}
|
|
9066
|
+
measure();
|
|
9067
|
+
}, [
|
|
9068
|
+
enabled,
|
|
9069
|
+
rowCount,
|
|
9070
|
+
data,
|
|
9071
|
+
measure
|
|
9072
|
+
]);
|
|
9073
|
+
(0, react$1.useLayoutEffect)(() => {
|
|
9074
|
+
if (!enabled) return;
|
|
9075
|
+
const content = contentRef.current;
|
|
9076
|
+
if (!content) return;
|
|
9077
|
+
const observer = new ResizeObserver(measure);
|
|
9078
|
+
observer.observe(content);
|
|
9079
|
+
const grid = content.querySelector(":scope > [data-leaderboard-grid]");
|
|
9080
|
+
if (grid) observer.observe(grid);
|
|
9081
|
+
return () => observer.disconnect();
|
|
9082
|
+
}, [
|
|
9083
|
+
enabled,
|
|
9084
|
+
rowCount,
|
|
9085
|
+
measure
|
|
9086
|
+
]);
|
|
9087
|
+
return {
|
|
9088
|
+
contentRef,
|
|
9089
|
+
fittedCount: enabled ? fittedCount : rowCount,
|
|
9090
|
+
isMeasurable
|
|
9091
|
+
};
|
|
9092
|
+
}
|
|
9093
|
+
//#endregion
|
|
9010
9094
|
//#region src/charts/leaderboard-chart/hooks/use-leaderboard-legend-items.ts
|
|
9011
9095
|
/**
|
|
9012
9096
|
* Hook to create legend items from leaderboard data
|
|
@@ -9067,11 +9151,13 @@ var leaderboard_chart_module_default = {
|
|
|
9067
9151
|
"deltaPlaceholder": "a8ccharts-GovfoW-deltaPlaceholder",
|
|
9068
9152
|
"deltaValue": "a8ccharts-GovfoW-deltaValue",
|
|
9069
9153
|
"emptyState": "a8ccharts-GovfoW-emptyState",
|
|
9154
|
+
"fitEmptyState": "a8ccharts-GovfoW-fitEmptyState",
|
|
9070
9155
|
"interactiveRow": "a8ccharts-GovfoW-interactiveRow",
|
|
9071
9156
|
"is-overlay": "a8ccharts-GovfoW-is-overlay",
|
|
9072
9157
|
"label": "a8ccharts-GovfoW-label",
|
|
9073
9158
|
"leaderboardChart": "a8ccharts-GovfoW-leaderboardChart",
|
|
9074
9159
|
"leaderboardChart__content": "a8ccharts-GovfoW-leaderboardChart__content",
|
|
9160
|
+
"leaderboardChart__content--fit": "a8ccharts-GovfoW-leaderboardChart__content--fit",
|
|
9075
9161
|
"leaderboardChart--loading": "a8ccharts-GovfoW-leaderboardChart--loading",
|
|
9076
9162
|
"leaderboardChart--responsive": "a8ccharts-GovfoW-leaderboardChart--responsive",
|
|
9077
9163
|
"overlap": "a8ccharts-GovfoW-overlap",
|
|
@@ -9116,8 +9202,9 @@ const defaultDeltaFormatter = (value) => {
|
|
|
9116
9202
|
* @param share - The bar's share of the row width, as a percentage.
|
|
9117
9203
|
* @return A CSS width value.
|
|
9118
9204
|
*/
|
|
9119
|
-
const getBarWidth = (share) => `calc(${share}% - var(--a8c
|
|
9120
|
-
const
|
|
9205
|
+
const getBarWidth = (share) => `calc(${share}% - var(--a8c-charts-dimension-leaderboard-bar-hover-inset, 0px) * ${share} / 100)`;
|
|
9206
|
+
const hasPreviousValue = (entry) => entry.previousValue != null && entry.previousShare != null;
|
|
9207
|
+
const hasDeltaValue = (entry) => hasPreviousValue(entry) && entry.delta != null;
|
|
9121
9208
|
const BarLabel = ({ label }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: typeof label === "string" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text$2, {
|
|
9122
9209
|
className: leaderboard_chart_module_default.label,
|
|
9123
9210
|
children: label
|
|
@@ -9135,7 +9222,7 @@ const BarWithLabel = ({ entry, withComparison, withOverlayLabel, primaryColor, s
|
|
|
9135
9222
|
backgroundColor: primaryColor
|
|
9136
9223
|
}
|
|
9137
9224
|
}),
|
|
9138
|
-
showComparisonBar &&
|
|
9225
|
+
showComparisonBar && hasPreviousValue(entry) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9139
9226
|
className: (0, clsx.default)(leaderboard_chart_module_default.bar, { [leaderboard_chart_module_default["bar--animated"]]: animation }),
|
|
9140
9227
|
style: {
|
|
9141
9228
|
width: getBarWidth(entry.previousShare),
|
|
@@ -9161,6 +9248,7 @@ const BarWithLabel = ({ entry, withComparison, withOverlayLabel, primaryColor, s
|
|
|
9161
9248
|
* @param props.valueFormatter - Custom formatter for values
|
|
9162
9249
|
* @param props.deltaFormatter - Custom formatter for delta values
|
|
9163
9250
|
* @param props.loading - Whether the chart is in loading state
|
|
9251
|
+
* @param props.fitRows - Whether to show only the rows that fit the chart's height
|
|
9164
9252
|
* @param props.animation - Whether the chart should animate on load
|
|
9165
9253
|
* @param props.showLegend - Whether to show legend
|
|
9166
9254
|
* @param props.legend - Legend configuration (orientation, position, alignment, shape, shapeStyles, interactive)
|
|
@@ -9171,7 +9259,7 @@ const BarWithLabel = ({ entry, withComparison, withOverlayLabel, primaryColor, s
|
|
|
9171
9259
|
* @param props.style - Custom styling for the chart container
|
|
9172
9260
|
* @return JSX element representing the leaderboard chart
|
|
9173
9261
|
*/
|
|
9174
|
-
const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propWidth, height: propHeight, withComparison = false, withOverlayLabel = false, primaryColor, secondaryColor, valueFormatter = defaultValueFormatter, deltaFormatter = defaultDeltaFormatter, animation, loading = false, showLegend = false, legend = {}, legendLabels, gap = "md", className, style, children }) => {
|
|
9262
|
+
const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propWidth, height: propHeight, withComparison = false, withOverlayLabel = false, primaryColor, secondaryColor, valueFormatter = defaultValueFormatter, deltaFormatter = defaultDeltaFormatter, animation, loading = false, fitRows = false, showLegend = false, legend = {}, legendLabels, gap = "md", className, style, children }) => {
|
|
9175
9263
|
const legendInteractive = legend.interactive ?? false;
|
|
9176
9264
|
const legendPosition = legend.position ?? "bottom";
|
|
9177
9265
|
const chartId = useChartId(providedChartId);
|
|
@@ -9240,6 +9328,8 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
|
|
|
9240
9328
|
}), [withComparison, withOverlayLabel])
|
|
9241
9329
|
});
|
|
9242
9330
|
const prefersReducedMotion = usePrefersReducedMotion();
|
|
9331
|
+
const { contentRef, fittedCount, isMeasurable } = useFittedRowCount(fitRows && !allSeriesHidden, data?.length ?? 0, data);
|
|
9332
|
+
const shouldFitRows = fitRows && isMeasurable;
|
|
9243
9333
|
if (!data || data.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SingleChartContext.Provider, {
|
|
9244
9334
|
value: { chartId },
|
|
9245
9335
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartLayout, {
|
|
@@ -9292,20 +9382,27 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
|
|
|
9292
9382
|
height: propHeight || void 0
|
|
9293
9383
|
},
|
|
9294
9384
|
trailingContent: nonLegendChildren,
|
|
9295
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
9296
|
-
|
|
9297
|
-
|
|
9385
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9386
|
+
ref: contentRef,
|
|
9387
|
+
className: (0, clsx.default)(leaderboard_chart_module_default.leaderboardChart__content, { [leaderboard_chart_module_default["leaderboardChart__content--fit"]]: shouldFitRows }),
|
|
9388
|
+
children: [shouldFitRows && fittedCount === 0 && !allSeriesHidden && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9389
|
+
className: (0, clsx.default)(leaderboard_chart_module_default.emptyState, leaderboard_chart_module_default.fitEmptyState),
|
|
9390
|
+
children: (0, _wordpress_i18n.__)("Not enough space to display data", "jetpack-charts")
|
|
9391
|
+
}), allSeriesHidden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9298
9392
|
className: leaderboard_chart_module_default.emptyState,
|
|
9299
9393
|
children: (0, _wordpress_i18n.__)("All series are hidden. Click legend items to show data.", "jetpack-charts")
|
|
9300
9394
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default, {
|
|
9301
9395
|
templateColumns: "minmax(0, 1fr) auto",
|
|
9302
9396
|
rowGap,
|
|
9303
9397
|
columnGap,
|
|
9304
|
-
|
|
9398
|
+
"data-leaderboard-grid": true,
|
|
9399
|
+
children: data.map((entry, rowIndex) => {
|
|
9400
|
+
const rowStyle = shouldFitRows && rowIndex >= fittedCount ? { visibility: "hidden" } : void 0;
|
|
9305
9401
|
const showComparisonColumn = withComparison && isComparisonVisible;
|
|
9306
|
-
const
|
|
9307
|
-
const
|
|
9308
|
-
const
|
|
9402
|
+
const hasPreviousPeriodValue = hasPreviousValue(entry);
|
|
9403
|
+
const hasDelta = hasDeltaValue(entry);
|
|
9404
|
+
const showComparisonValue = showComparisonColumn && hasDelta;
|
|
9405
|
+
const showComparisonPlaceholder = showComparisonColumn && !hasDelta;
|
|
9309
9406
|
const colorIndex = showComparisonValue ? Math.sign(entry.delta) + 1 : 1;
|
|
9310
9407
|
const deltaColor = deltaColors[colorIndex];
|
|
9311
9408
|
const rowCells = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stack, {
|
|
@@ -9337,16 +9434,18 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
|
|
|
9337
9434
|
style: { color: deltaColor },
|
|
9338
9435
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9339
9436
|
"aria-hidden": "true",
|
|
9340
|
-
children: "
|
|
9437
|
+
children: "—"
|
|
9341
9438
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default$1, {
|
|
9342
9439
|
as: "span",
|
|
9343
|
-
children: (0, _wordpress_i18n.__)("No comparison data", "jetpack-charts")
|
|
9440
|
+
children: hasPreviousPeriodValue ? (0, _wordpress_i18n.__)("Percentage change unavailable", "jetpack-charts") : (0, _wordpress_i18n.__)("No comparison data", "jetpack-charts")
|
|
9344
9441
|
})]
|
|
9345
9442
|
})
|
|
9346
9443
|
]
|
|
9347
9444
|
})] });
|
|
9348
9445
|
if (entry.onClick) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
9349
9446
|
type: "button",
|
|
9447
|
+
"data-row-index": rowIndex,
|
|
9448
|
+
style: rowStyle,
|
|
9350
9449
|
className: (0, clsx.default)(leaderboard_chart_module_default.row, leaderboard_chart_module_default.interactiveRow),
|
|
9351
9450
|
onClick: entry.onClick,
|
|
9352
9451
|
"aria-label": entry.ariaLabel,
|
|
@@ -9357,11 +9456,13 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
|
|
|
9357
9456
|
})]
|
|
9358
9457
|
}, entry.id);
|
|
9359
9458
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9459
|
+
"data-row-index": rowIndex,
|
|
9460
|
+
style: rowStyle,
|
|
9360
9461
|
className: leaderboard_chart_module_default.row,
|
|
9361
9462
|
children: rowCells
|
|
9362
9463
|
}, entry.id);
|
|
9363
9464
|
})
|
|
9364
|
-
})
|
|
9465
|
+
})]
|
|
9365
9466
|
})
|
|
9366
9467
|
})
|
|
9367
9468
|
});
|