@automattic/charts 3.2.0 → 4.1.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 +47 -0
- package/SECURITY.md +1 -0
- package/TOKENS.md +53 -58
- package/dist/index.cjs +1483 -664
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +93 -29
- package/dist/index.d.cts +233 -247
- package/dist/index.d.ts +233 -247
- package/dist/index.js +1484 -667
- package/dist/index.js.map +1 -1
- package/package.json +7 -6
- package/src/charts/area-chart/area-chart.tsx +39 -25
- package/src/charts/area-chart/test/area-chart.test.tsx +4 -33
- package/src/charts/area-chart/test/tick-placement.test.tsx +55 -0
- package/src/charts/area-chart/test/tooltip-resolution-branches.test.tsx +86 -0
- package/src/charts/area-chart/test/tooltip-resolution.test.tsx +69 -0
- package/src/charts/area-chart/types.ts +4 -7
- package/src/charts/bar-chart/bar-chart.tsx +2 -1
- package/src/charts/bar-chart/private/test/use-bar-chart-options-formatting.test.tsx +69 -0
- package/src/charts/bar-chart/private/use-bar-chart-options.ts +27 -15
- package/src/charts/bar-list-chart/bar-list-chart.tsx +5 -0
- package/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss +33 -23
- package/src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx +138 -182
- package/src/charts/conversion-funnel-chart/test/conversion-funnel-chart.test.tsx +46 -0
- package/src/charts/geo-chart/geo-chart.module.scss +1 -0
- package/src/charts/geo-chart/geo-chart.tsx +5 -10
- package/src/charts/geo-chart/test/geo-chart.test.tsx +2 -2
- package/src/charts/heatmap-chart/heatmap-chart.module.scss +52 -16
- package/src/charts/heatmap-chart/heatmap-chart.tsx +89 -37
- package/src/charts/heatmap-chart/index.ts +2 -1
- package/src/charts/heatmap-chart/private/build-calendar-data.ts +126 -89
- package/src/charts/heatmap-chart/private/civil-day.ts +174 -0
- package/src/charts/heatmap-chart/private/heatmap-legend.tsx +11 -4
- package/src/charts/heatmap-chart/private/index.ts +1 -1
- package/src/charts/heatmap-chart/private/use-heatmap-colors.ts +5 -1
- package/src/charts/heatmap-chart/test/build-calendar-data.test.ts +3 -0
- package/src/charts/heatmap-chart/test/civil-day.test.ts +70 -0
- package/src/charts/heatmap-chart/test/heatmap-chart.test.tsx +142 -12
- package/src/charts/heatmap-chart/test/use-calendar-heatmap-data.test.tsx +118 -0
- package/src/charts/heatmap-chart/test/use-heatmap-colors.test.ts +8 -0
- package/src/charts/heatmap-chart/test/zoned-calendar-data.test.ts +248 -0
- package/src/charts/heatmap-chart/types.ts +8 -2
- package/src/charts/heatmap-chart/use-calendar-heatmap-data.ts +42 -0
- package/src/charts/leaderboard-chart/hooks/use-fitted-row-count.ts +2 -2
- package/src/charts/leaderboard-chart/hooks/use-leaderboard-legend-items.ts +3 -5
- package/src/charts/leaderboard-chart/leaderboard-chart.module.scss +31 -19
- package/src/charts/leaderboard-chart/leaderboard-chart.tsx +27 -17
- package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +14 -0
- package/src/charts/leaderboard-chart/test/use-leaderboard-legend-items.test.tsx +2 -9
- package/src/charts/leaderboard-chart/types.ts +2 -2
- package/src/charts/line-chart/line-chart.module.scss +10 -18
- package/src/charts/line-chart/line-chart.tsx +128 -26
- package/src/charts/line-chart/private/line-chart-annotation.tsx +18 -19
- package/src/charts/line-chart/test/crosshair-style-types.test.ts +31 -0
- package/src/charts/line-chart/test/default-tooltip-formatting.test.tsx +74 -0
- package/src/charts/line-chart/test/line-chart.test.tsx +174 -41
- package/src/charts/line-chart/test/tick-placement.test.tsx +84 -0
- package/src/charts/line-chart/test/tooltip-resolution.test.tsx +140 -0
- package/src/charts/line-chart/test/zoned-date-string.test.tsx +37 -0
- package/src/charts/line-chart/types.ts +17 -3
- package/src/charts/pie-chart/pie-chart.module.scss +13 -1
- package/src/charts/pie-chart/pie-chart.tsx +30 -40
- package/src/charts/pie-chart/test/pie-chart.test.tsx +59 -12
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss +8 -3
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +23 -37
- package/src/charts/pie-semi-circle-chart/test/pie-semi-circle-chart.test.tsx +50 -0
- package/src/charts/private/chart-layout/chart-layout.module.scss +3 -2
- package/src/charts/private/svg-empty-state/svg-empty-state.module.scss +1 -1
- package/src/charts/private/test/axis-formatting.test.tsx +85 -0
- package/src/charts/private/test/time-axis-formatting.test.ts +109 -0
- package/src/charts/private/test/time-axis-options.test.ts +267 -0
- package/src/charts/private/test/time-axis-tick-invariants.test.ts +153 -0
- package/src/charts/private/test/time-axis-tick-values.test.ts +280 -0
- package/src/charts/private/test/time-axis.test.ts +9 -69
- package/src/charts/private/time-axis-options.ts +106 -0
- package/src/charts/private/time-axis.ts +318 -136
- package/src/charts/private/x-zoom/x-zoom.module.scss +3 -3
- package/src/charts/private/xy-plot/xy-plot.module.scss +8 -0
- package/src/charts/sparkline/test/sparkline.test.tsx +29 -56
- package/src/charts/sparkline/types.ts +1 -1
- package/src/components/legend/private/base-legend.module.scss +9 -5
- package/src/components/tooltip/accessible-tooltip.tsx +21 -17
- package/src/components/tooltip/base-tooltip.module.scss +3 -3
- package/src/components/tooltip/private/bounded-tooltip.tsx +227 -0
- package/src/components/tooltip/test/accessible-tooltip.test.tsx +30 -4
- package/src/components/tooltip/test/bounded-tooltip.test.tsx +225 -0
- package/src/components/tooltip/test/xy-chart-tooltip.test.tsx +379 -0
- package/src/components/tooltip/xy-chart-tooltip.tsx +326 -0
- package/src/components/trend-indicator/trend-indicator.module.scss +2 -2
- package/src/hooks/test/use-chart-data-transform-zone.test.tsx +36 -0
- package/src/hooks/test/use-chart-margin.test.tsx +173 -3
- package/src/hooks/test/use-xychart-theme.test.tsx +16 -3
- package/src/hooks/use-chart-data-transform.ts +10 -3
- package/src/hooks/use-chart-margin.tsx +54 -9
- package/src/hooks/use-xychart-theme.ts +25 -9
- package/src/index.ts +8 -0
- package/src/providers/chart-context/global-charts-provider.tsx +37 -38
- package/src/providers/chart-context/hooks/use-chart-formatting.ts +22 -0
- package/src/providers/chart-context/index.ts +1 -0
- package/src/providers/chart-context/private/catalog-pointers.ts +42 -0
- package/src/providers/chart-context/private/series-palette.ts +3 -13
- package/src/providers/chart-context/test/{sparse-theme.test.ts → catalog-pointers.test.ts} +22 -10
- package/src/providers/chart-context/test/chart-context.test.tsx +177 -792
- package/src/providers/chart-context/test/use-global-charts-theme.test.tsx +7 -25
- package/src/providers/chart-context/themes.ts +7 -52
- package/src/providers/chart-context/types.ts +3 -0
- package/src/styles/chart-scope.scss +39 -62
- package/src/styles/test/chart-scope.test.ts +19 -47
- package/src/test-utils/series-fixtures.ts +31 -0
- package/src/test-utils/zoned-parsing-suite.ts +90 -0
- package/src/types.ts +79 -166
- package/src/utils/bucket-info.ts +87 -0
- package/src/utils/color-utils.ts +0 -27
- package/src/utils/date-formatting.ts +157 -0
- package/src/utils/date-parsing.ts +103 -52
- package/src/utils/get-edge-tick-widths.ts +33 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/test/bucket-info.test.ts +185 -0
- package/src/utils/test/color-utils.test.ts +1 -48
- package/src/utils/test/date-formatting.test.ts +89 -0
- package/src/utils/test/date-parsing.auckland.test.ts +6 -0
- package/src/utils/test/date-parsing.santiago.test.ts +6 -0
- package/src/utils/test/date-parsing.test.ts +16 -1
- package/src/utils/test/get-edge-tick-widths.test.ts +55 -0
- package/src/utils/test/get-styles.test.ts +10 -7
- package/src/utils/test/merge-themes.test.ts +18 -46
- package/src/utils/warn-once.ts +23 -0
- package/src/visx/types.ts +56 -5
- package/src/hooks/test/use-xychart-theme-provider-override.test.tsx +0 -28
- package/src/providers/chart-context/private/theme-override-vars.ts +0 -131
- package/src/providers/chart-context/private/with-catalog-pointers.ts +0 -93
- package/src/providers/chart-context/test/theme-override-vars-guard.test.tsx +0 -29
- package/src/providers/chart-context/test/theme-override-vars.test.ts +0 -183
- package/src/providers/chart-context/test/with-catalog-pointers.test.ts +0 -104
- package/src/styles/test/wpds-fallbacks.test.ts +0 -131
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,7 @@ let react = require("react");
|
|
|
31
31
|
react = __toESM(react);
|
|
32
32
|
let _visx_vendor_d3_color = require("@visx/vendor/d3-color");
|
|
33
33
|
let date_fns = require("date-fns");
|
|
34
|
+
let _date_fns_tz = require("@date-fns/tz");
|
|
34
35
|
let _visx_text = require("@visx/text");
|
|
35
36
|
let deepmerge = require("deepmerge");
|
|
36
37
|
deepmerge = __toESM(deepmerge, 1);
|
|
@@ -40,6 +41,7 @@ let _visx_scale = require("@visx/scale");
|
|
|
40
41
|
let _visx_group = require("@visx/group");
|
|
41
42
|
let _visx_legend = require("@visx/legend");
|
|
42
43
|
let _wordpress_ui = require("@wordpress/ui");
|
|
44
|
+
let react_dom = require("react-dom");
|
|
43
45
|
let _visx_gradient = require("@visx/gradient");
|
|
44
46
|
let _visx_curve = require("@visx/curve");
|
|
45
47
|
let _visx_responsive = require("@visx/responsive");
|
|
@@ -85,46 +87,35 @@ function attachSubComponents(Chart, subComponents) {
|
|
|
85
87
|
return Object.assign(Chart, subComponents);
|
|
86
88
|
}
|
|
87
89
|
//#endregion
|
|
88
|
-
//#region src/utils/
|
|
90
|
+
//#region src/utils/warn-once.ts
|
|
91
|
+
const warned = /* @__PURE__ */ new Set();
|
|
89
92
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* This module provides utilities for parsing various date string formats and converting
|
|
93
|
-
* them to local timezone dates using the battle-tested date-fns library. For formats
|
|
94
|
-
* without timezone info, they're treated as local. For formats with timezone info,
|
|
95
|
-
* they're converted to the equivalent local time.
|
|
93
|
+
* Warns about a host-supplied value once per process, and never in production.
|
|
96
94
|
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
95
|
+
* @param key - Identifies the value, so a bad prop on every point warns once.
|
|
96
|
+
* @param message - What the host got wrong and what happens instead.
|
|
97
|
+
*/
|
|
98
|
+
const warnOnce = (key, message) => {
|
|
99
|
+
if (warned.has(key) || process.env.NODE_ENV === "production") return;
|
|
100
|
+
warned.add(key);
|
|
101
|
+
console.warn(`[Charts] ${message}`);
|
|
102
|
+
};
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/utils/date-parsing.ts
|
|
105
|
+
/**
|
|
106
|
+
* @file Date parsing: a naive string is dated in a supplied IANA zone, or the runtime's own
|
|
105
107
|
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* - YYYY-MM-DD HH:mm (treated as local)
|
|
110
|
-
* - YYYY-MM-DDTHH:mm:ss (treated as local)
|
|
111
|
-
* - YYYY-MM-DDTHH:mm:ss.SSS (treated as local)
|
|
112
|
-
* - YYYY-MM-DDTHH:mm (treated as local)
|
|
113
|
-
* - YYYY-MM-DDTHH:mm:ssZ (converted to local)
|
|
114
|
-
* - YYYY-MM-DDTHH:mm:ss±HH:mm (converted to local)
|
|
108
|
+
* A string carrying an offset is already an instant and parses the same everywhere. A string
|
|
109
|
+
* without one is only a wall-clock reading, so it means nothing until a zone is named. See
|
|
110
|
+
* `parseAsLocalDate` for the supported formats.
|
|
115
111
|
*
|
|
116
|
-
*
|
|
117
|
-
* ```typescript
|
|
118
|
-
* parseAsLocalDate("2025-01-01"); // Local timezone
|
|
119
|
-
* parseAsLocalDate("2025-01-01 14:30:00"); // Local timezone
|
|
120
|
-
* parseAsLocalDate("2025-01-01 14:30"); // Local timezone
|
|
121
|
-
* parseAsLocalDate("2025-01-01T14:30:45.123"); // Local timezone
|
|
122
|
-
* parseAsLocalDate("2025-01-01T14:30:00Z"); // UTC 14:30 → Local equivalent
|
|
123
|
-
* parseAsLocalDate("2025-01-01T14:30:00+05:00"); // +05:00 14:30 → Local equivalent
|
|
124
|
-
* ```
|
|
112
|
+
* Note: this specifically avoids date-fns's default of parsing `YYYY-MM-DD` as a UTC date.
|
|
125
113
|
*/
|
|
126
114
|
/**
|
|
127
115
|
* Checks if a date string contains timezone information
|
|
116
|
+
*
|
|
117
|
+
* The minutes are optional because ISO 8601 allows a bare `±hh`, which `parseISO` reads.
|
|
118
|
+
*
|
|
128
119
|
* @param {string} dateString - The date string to check for timezone information
|
|
129
120
|
* @return {boolean} True if the date string contains timezone information, false otherwise
|
|
130
121
|
*/
|
|
@@ -132,28 +123,57 @@ const hasTimezone = (dateString) => {
|
|
|
132
123
|
const tIndex = dateString.indexOf("T");
|
|
133
124
|
if (tIndex === -1) return false;
|
|
134
125
|
if (dateString.endsWith("Z")) return true;
|
|
135
|
-
return /[+-]\d{2}
|
|
126
|
+
return /[+-]\d{2}(?::?\d{2})?$/.test(dateString.slice(tIndex + 1));
|
|
127
|
+
};
|
|
128
|
+
const NAIVE = /^(\d{1,4})-(\d{1,2})-(\d{1,2})(?:[T ](\d{1,2}):(\d{1,2})(?::(\d{1,2})(?:\.(\d{1,3}))?)?)?$/;
|
|
129
|
+
const asUtcMs = ([, ...fields]) => {
|
|
130
|
+
const [year, month, day, hour, minute, second, ms] = fields.map((field) => Number(field ?? 0));
|
|
131
|
+
const date = /* @__PURE__ */ new Date(0);
|
|
132
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
133
|
+
date.setUTCHours(hour, minute, second, ms);
|
|
134
|
+
return date.getTime();
|
|
136
135
|
};
|
|
137
136
|
/**
|
|
138
|
-
*
|
|
137
|
+
* The instant a wall-clock reading names in a given zone.
|
|
139
138
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* local timezone equivalent.
|
|
139
|
+
* A reading a spring-forward gap deleted moves forward past the gap, and one a fall-back
|
|
140
|
+
* repeated resolves to a single instant. Neither is configurable: see CHARTS-268.
|
|
143
141
|
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
* - YYYY-MM-DD HH:mm (local)
|
|
148
|
-
* - YYYY-MM-DDTHH:mm:ss (local)
|
|
149
|
-
* - YYYY-MM-DDTHH:mm:ss.SSS (local)
|
|
150
|
-
* - YYYY-MM-DDTHH:mm (local)
|
|
151
|
-
* - YYYY-MM-DDTHH:mm:ssZ (UTC → local)
|
|
152
|
-
* - YYYY-MM-DDTHH:mm:ss±HH:mm (offset → local)
|
|
153
|
-
* @param {string} dateString - The date string to parse into a local timezone date
|
|
154
|
-
* @return {Date} A Date object representing the parsed date in local timezone, or an invalid Date if parsing fails
|
|
142
|
+
* @param wallClock - The reading, as the milliseconds it would be if it were UTC.
|
|
143
|
+
* @param timeZone - IANA zone the reading is dated in.
|
|
144
|
+
* @return The instant, or `null` where `timeZone` is not a zone Intl accepts.
|
|
155
145
|
*/
|
|
156
|
-
const
|
|
146
|
+
const wallClockToInstant = (wallClock, timeZone) => {
|
|
147
|
+
const first = (0, _date_fns_tz.tzOffset)(timeZone, new Date(wallClock));
|
|
148
|
+
if (Number.isNaN(first)) {
|
|
149
|
+
warnOnce(`parse:timeZone:${timeZone}`, `timeZone ${JSON.stringify(timeZone)} is not a zone Intl accepts, so dates are read in the browser's zone. Pass an IANA name or a UTC offset such as "+05:30".`);
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
const guess = wallClock - first * 6e4;
|
|
153
|
+
const second = (0, _date_fns_tz.tzOffset)(timeZone, new Date(guess));
|
|
154
|
+
if (first === second) return new Date(guess);
|
|
155
|
+
const corrected = wallClock - second * 6e4;
|
|
156
|
+
const third = (0, _date_fns_tz.tzOffset)(timeZone, new Date(corrected));
|
|
157
|
+
if (second === third) return new Date(corrected);
|
|
158
|
+
return /* @__PURE__ */ new Date(wallClock - Math.min(second, third) * 6e4);
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Parses any supported date string format into an instant, dated in `timeZone` or the runtime's own
|
|
162
|
+
*
|
|
163
|
+
* Uses date-fns for robust parsing and validation. A string carrying timezone info
|
|
164
|
+
* is already an instant and is returned as one, whatever `timeZone` says. A string
|
|
165
|
+
* without it is a wall-clock reading, dated in `timeZone` when one is supplied and
|
|
166
|
+
* in the runtime's own zone when none is.
|
|
167
|
+
*
|
|
168
|
+
* A wall clock a DST gap deleted is moved forward past the gap; one a fall-back repeated
|
|
169
|
+
* resolves to a single instant. Neither policy is selectable. A zone `Intl` rejects falls
|
|
170
|
+
* back to the runtime's own, and warns once outside production.
|
|
171
|
+
*
|
|
172
|
+
* @param {string} dateString - The date string to parse into a date
|
|
173
|
+
* @param {string} [timeZone] - IANA zone a naive string is read in; the runtime's own when absent, and ignored by a string that carries its own offset
|
|
174
|
+
* @return {Date} A Date object representing the parsed instant, or an invalid Date if parsing fails
|
|
175
|
+
*/
|
|
176
|
+
const parseAsLocalDate = (dateString, timeZone) => {
|
|
157
177
|
const trimmedString = dateString.trim();
|
|
158
178
|
if (hasTimezone(trimmedString)) {
|
|
159
179
|
const isoDate = (0, date_fns.parseISO)(trimmedString);
|
|
@@ -169,11 +189,47 @@ const parseAsLocalDate = (dateString) => {
|
|
|
169
189
|
"yyyy-MM-dd'T'HH:mm"
|
|
170
190
|
]) {
|
|
171
191
|
const result = (0, date_fns.parse)(trimmedString, format, /* @__PURE__ */ new Date());
|
|
172
|
-
if ((0, date_fns.isValid)(result))
|
|
192
|
+
if (!(0, date_fns.isValid)(result)) continue;
|
|
193
|
+
if (!timeZone) return result;
|
|
194
|
+
const fields = NAIVE.exec(trimmedString);
|
|
195
|
+
return (fields && wallClockToInstant(asUtcMs(fields), timeZone)) ?? result;
|
|
173
196
|
}
|
|
174
197
|
return /* @__PURE__ */ new Date(NaN);
|
|
175
198
|
};
|
|
176
199
|
//#endregion
|
|
200
|
+
//#region src/utils/bucket-info.ts
|
|
201
|
+
const getInstant = (point) => {
|
|
202
|
+
if (!point) return;
|
|
203
|
+
if ("date" in point && point.date) return point.date.getTime();
|
|
204
|
+
if ("dateString" in point && point.dateString) return parseAsLocalDate(point.dateString).getTime();
|
|
205
|
+
};
|
|
206
|
+
const getPointSpacingInHours = (data) => {
|
|
207
|
+
return data.reduce((spacing, datom) => {
|
|
208
|
+
const instants = [...new Set(datom.data.map((point) => getInstant(point)))].filter((time) => time !== void 0 && Number.isFinite(time)).sort((a, b) => a - b);
|
|
209
|
+
return instants.reduce((seriesSpacing, time, index) => index === 0 ? seriesSpacing : Math.min(seriesSpacing, Math.abs((0, date_fns.differenceInHours)(time, instants[index - 1]))), spacing);
|
|
210
|
+
}, Number.POSITIVE_INFINITY);
|
|
211
|
+
};
|
|
212
|
+
const SUB_DAILY_SPACING_HOURS = 23;
|
|
213
|
+
const MONTHLY_SPACING_HOURS = 672;
|
|
214
|
+
const getBucketResolution = (data, tickResolution) => {
|
|
215
|
+
if (tickResolution) return tickResolution === "week" ? "day" : tickResolution;
|
|
216
|
+
const spacingInHours = getPointSpacingInHours(data);
|
|
217
|
+
if (spacingInHours < SUB_DAILY_SPACING_HOURS) return "hour";
|
|
218
|
+
if (!Number.isFinite(spacingInHours) || spacingInHours < MONTHLY_SPACING_HOURS) return "day";
|
|
219
|
+
return spacingInHours < 12 * MONTHLY_SPACING_HOURS ? "month" : "year";
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* How this data was classified, for consumers that render their own labels.
|
|
223
|
+
*
|
|
224
|
+
* @param data - The series the chart draws, in any order, dated by `date` or `dateString`.
|
|
225
|
+
* @param tickResolution - Caller-declared bucket resolution, when known.
|
|
226
|
+
* @return The declared or inferred bucket, and the resolution formats key on.
|
|
227
|
+
*/
|
|
228
|
+
const getBucketInfo = (data, tickResolution) => ({
|
|
229
|
+
bucket: tickResolution ?? getBucketResolution(data),
|
|
230
|
+
displayResolution: getBucketResolution(data, tickResolution)
|
|
231
|
+
});
|
|
232
|
+
//#endregion
|
|
177
233
|
//#region src/utils/format-metric-value.ts
|
|
178
234
|
/**
|
|
179
235
|
* Format a numeric metric value based on type, precision and scale.
|
|
@@ -241,6 +297,31 @@ const formatPercentage = (value) => {
|
|
|
241
297
|
} });
|
|
242
298
|
};
|
|
243
299
|
//#endregion
|
|
300
|
+
//#region src/utils/get-edge-tick-widths.ts
|
|
301
|
+
/**
|
|
302
|
+
* Rendered widths of the first and last tick labels on an axis.
|
|
303
|
+
*
|
|
304
|
+
* An unmeasurable label reserves nothing, which is what a width of 0 already
|
|
305
|
+
* means to every caller, so it is reported as 0 rather than as its own case.
|
|
306
|
+
*
|
|
307
|
+
* @param ticks - Tick values, in axis order.
|
|
308
|
+
* @param formatTick - Function to format a tick.
|
|
309
|
+
* @param {object} labelStyle - Style object for the label.
|
|
310
|
+
* @return {object} - Widths in pixels.
|
|
311
|
+
*/
|
|
312
|
+
const getEdgeTickWidths = (ticks, formatTick, labelStyle) => {
|
|
313
|
+
if (!ticks.length) return {
|
|
314
|
+
first: 0,
|
|
315
|
+
last: 0
|
|
316
|
+
};
|
|
317
|
+
const lastIndex = ticks.length - 1;
|
|
318
|
+
const label = (tick, index) => String(formatTick ? formatTick(tick, index, []) ?? "" : tick);
|
|
319
|
+
return {
|
|
320
|
+
first: (0, _visx_text.getStringWidth)(label(ticks[0], 0), labelStyle) ?? 0,
|
|
321
|
+
last: (0, _visx_text.getStringWidth)(label(ticks[lastIndex], lastIndex), labelStyle) ?? 0
|
|
322
|
+
};
|
|
323
|
+
};
|
|
324
|
+
//#endregion
|
|
244
325
|
//#region src/utils/get-longest-tick-width.ts
|
|
245
326
|
/**
|
|
246
327
|
* Returns the width of the longest tick.
|
|
@@ -386,20 +467,6 @@ const parseHslString = (hslString) => {
|
|
|
386
467
|
];
|
|
387
468
|
};
|
|
388
469
|
/**
|
|
389
|
-
* Parse an RGB string like 'rgb(255, 0, 0)' into a hex color.
|
|
390
|
-
*
|
|
391
|
-
* @deprecated Use normalizeColorToHex() instead, which handles all color formats including rgb() and rgba().
|
|
392
|
-
* @param rgbString - RGB color string (not RGBA)
|
|
393
|
-
* @return hex color string or null if invalid
|
|
394
|
-
*/
|
|
395
|
-
const parseRgbString = (rgbString) => {
|
|
396
|
-
const lower = rgbString.toLowerCase().trim();
|
|
397
|
-
if (!lower.startsWith("rgb(") || lower.startsWith("rgba(")) return null;
|
|
398
|
-
const parsed = (0, _visx_vendor_d3_color.color)(lower);
|
|
399
|
-
if (!parsed) return null;
|
|
400
|
-
return parsed.formatHex();
|
|
401
|
-
};
|
|
402
|
-
/**
|
|
403
470
|
* Normalize any CSS color value to a hex color string.
|
|
404
471
|
* Handles hex, HSL, HSLA, RGB, RGBA, named CSS colors, and CSS variables.
|
|
405
472
|
*
|
|
@@ -627,6 +694,106 @@ const resolveFontSize = (val) => {
|
|
|
627
694
|
}
|
|
628
695
|
};
|
|
629
696
|
//#endregion
|
|
697
|
+
//#region src/utils/date-formatting.ts
|
|
698
|
+
const formatters = /* @__PURE__ */ new Map();
|
|
699
|
+
const getFormatter$1 = (options, { locale, timeZone }) => {
|
|
700
|
+
const key = `${locale ?? ""}|${timeZone ?? ""}|${JSON.stringify(options)}`;
|
|
701
|
+
let formatter = formatters.get(key);
|
|
702
|
+
if (!formatter) {
|
|
703
|
+
formatter = new Intl.DateTimeFormat(locale, {
|
|
704
|
+
...options,
|
|
705
|
+
timeZone
|
|
706
|
+
});
|
|
707
|
+
formatters.set(key, formatter);
|
|
708
|
+
}
|
|
709
|
+
return formatter;
|
|
710
|
+
};
|
|
711
|
+
const isUsable = (formatting) => {
|
|
712
|
+
try {
|
|
713
|
+
Intl.DateTimeFormat(formatting.locale, { timeZone: formatting.timeZone });
|
|
714
|
+
return true;
|
|
715
|
+
} catch {
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
const toBcp47 = (locale) => locale?.replace(/_/g, "-");
|
|
720
|
+
/**
|
|
721
|
+
* Repairs a host locale for `Intl`, dropping either half it still cannot use.
|
|
722
|
+
*
|
|
723
|
+
* Every formatter below is built during render, so an unusable string would throw
|
|
724
|
+
* where React unmounts the whole host tree rather than one chart. WordPress hands
|
|
725
|
+
* out the likeliest one either way: `timezone_string` is `''` on a site set to a
|
|
726
|
+
* raw UTC offset, and has no repair.
|
|
727
|
+
*
|
|
728
|
+
* @param formatting - Locale and time zone as the host supplied them.
|
|
729
|
+
* @return The usable halves, each `undefined` where the host's value was not.
|
|
730
|
+
*/
|
|
731
|
+
const sanitizeFormatting = (formatting) => {
|
|
732
|
+
const repaired = {
|
|
733
|
+
...formatting,
|
|
734
|
+
locale: toBcp47(formatting.locale)
|
|
735
|
+
};
|
|
736
|
+
if (isUsable(repaired)) return repaired;
|
|
737
|
+
const locale = isUsable({ locale: repaired.locale }) ? repaired.locale : void 0;
|
|
738
|
+
const timeZone = isUsable({ timeZone: repaired.timeZone }) ? repaired.timeZone : void 0;
|
|
739
|
+
if (locale === void 0 && formatting.locale !== void 0) warnOnce(`locale:${formatting.locale}`, `locale ${JSON.stringify(formatting.locale)} is not a BCP-47 tag Intl accepts, so dates render in the browser's locale.`);
|
|
740
|
+
if (timeZone === void 0 && formatting.timeZone !== void 0) warnOnce(`timeZone:${formatting.timeZone}`, `timeZone ${JSON.stringify(formatting.timeZone)} is not a zone Intl accepts, so dates render in the browser's zone. Pass an IANA name or a UTC offset such as "+05:30".`);
|
|
741
|
+
return {
|
|
742
|
+
locale,
|
|
743
|
+
timeZone
|
|
744
|
+
};
|
|
745
|
+
};
|
|
746
|
+
/**
|
|
747
|
+
* A date formatter bound to the host's locale and time zone.
|
|
748
|
+
*
|
|
749
|
+
* @param options - Date-part options, as for `Intl.DateTimeFormat`.
|
|
750
|
+
* @param formatting - Host locale and time zone; either half absent leaves the runtime's own.
|
|
751
|
+
* @return Formatter taking a timestamp or a `Date`.
|
|
752
|
+
*/
|
|
753
|
+
const createDateFormatter = (options, formatting) => {
|
|
754
|
+
return (value) => {
|
|
755
|
+
if (!Number.isFinite(Number(value))) return new Date(Number(value)).toLocaleDateString();
|
|
756
|
+
return getFormatter$1(options, formatting).format(value);
|
|
757
|
+
};
|
|
758
|
+
};
|
|
759
|
+
const CLOCK_OPTIONS = {
|
|
760
|
+
month: "numeric",
|
|
761
|
+
hour: "numeric",
|
|
762
|
+
hourCycle: "h23"
|
|
763
|
+
};
|
|
764
|
+
const NO_FIELDS = {
|
|
765
|
+
month: NaN,
|
|
766
|
+
hour: NaN
|
|
767
|
+
};
|
|
768
|
+
/**
|
|
769
|
+
* A clock reading calendar fields in the host's time zone.
|
|
770
|
+
*
|
|
771
|
+
* `Date`'s own getters read the browser's zone, so a chart viewed from another
|
|
772
|
+
* country anchors its ticks to the wrong midnights and the wrong January
|
|
773
|
+
* without this.
|
|
774
|
+
*
|
|
775
|
+
* @param timeZone - IANA zone name; absent leaves the runtime's own.
|
|
776
|
+
* @return Reader taking a `Date`.
|
|
777
|
+
*/
|
|
778
|
+
const createZonedClock = (timeZone) => {
|
|
779
|
+
if (!timeZone) return (date) => ({
|
|
780
|
+
month: date.getMonth() + 1,
|
|
781
|
+
hour: date.getHours()
|
|
782
|
+
});
|
|
783
|
+
return (date) => {
|
|
784
|
+
if (!Number.isFinite(Number(date))) return NO_FIELDS;
|
|
785
|
+
const parts = getFormatter$1(CLOCK_OPTIONS, {
|
|
786
|
+
locale: "en-US",
|
|
787
|
+
timeZone
|
|
788
|
+
}).formatToParts(date);
|
|
789
|
+
const read = (type) => Number(parts.find((part) => part.type === type)?.value);
|
|
790
|
+
return {
|
|
791
|
+
month: read("month"),
|
|
792
|
+
hour: read("hour")
|
|
793
|
+
};
|
|
794
|
+
};
|
|
795
|
+
};
|
|
796
|
+
//#endregion
|
|
630
797
|
//#region src/providers/chart-scope/chart-scope-context.ts
|
|
631
798
|
/**
|
|
632
799
|
* The nearest chart element, published so JS token resolution reads the same CSS cascade that paints the chart's own colours.
|
|
@@ -774,150 +941,29 @@ const SERIES_SLOT_1_FALLBACK = "#3858e9";
|
|
|
774
941
|
/** The catalog pointer for every slot, in slot order. */
|
|
775
942
|
const SERIES_PALETTE_POINTERS = Array.from({ length: 5 }, (_, index) => index === 0 ? `var(${seriesRole(1)}, ${SERIES_SLOT_1_FALLBACK})` : `var(${seriesRole(index + 1)})`);
|
|
776
943
|
//#endregion
|
|
777
|
-
//#region src/providers/chart-context/private/theme-override-vars.ts
|
|
778
|
-
const ROLE_FOR_FIELD = [
|
|
779
|
-
["--a8c-charts-color-background", (theme) => theme.backgroundColor],
|
|
780
|
-
["--a8c-charts-color-grid", (theme) => theme.gridStyles?.stroke],
|
|
781
|
-
["--a8c-charts-color-axis", (theme) => theme.xAxisLineStyles?.stroke],
|
|
782
|
-
["--a8c-charts-color-tick", (theme) => theme.xTickLineStyles?.stroke],
|
|
783
|
-
["--a8c-charts-color-label-axis", (theme) => theme.svgLabelSmall?.fill],
|
|
784
|
-
["--a8c-charts-color-label-background", (theme) => theme.labelBackgroundColor],
|
|
785
|
-
["--a8c-charts-color-label-on-fill", (theme) => theme.labelTextColor],
|
|
786
|
-
...Array.from({ length: 5 }, (_, index) => [seriesRole(index + 1), (theme) => theme.colors?.[index]])
|
|
787
|
-
];
|
|
788
|
-
let warnedAboutExtraColors = false;
|
|
789
|
-
/**
|
|
790
|
-
* Warns once when a consumer passes more colors than there are slots to publish them into.
|
|
791
|
-
*
|
|
792
|
-
* @param count - How many entries `theme.colors` holds.
|
|
793
|
-
*/
|
|
794
|
-
function warnOnceAboutExtraColors(count) {
|
|
795
|
-
if (warnedAboutExtraColors || process.env.NODE_ENV === "production") return;
|
|
796
|
-
warnedAboutExtraColors = true;
|
|
797
|
-
console.warn(`[Charts] theme.colors holds ${count} colors and the palette has 5 slots, so entries past the 5th are ignored. Set a per-series color with \`options.stroke\` instead.`);
|
|
798
|
-
}
|
|
799
|
-
/**
|
|
800
|
-
* The variable a `theme` prop override is published as, one layer outside the role itself.
|
|
801
|
-
*
|
|
802
|
-
* `chart-scope.scss` declares each of these roles as `var(<role>-theme, <catalog default>)`, so an override that is invalid at computed-value time — a fallback-less `var()` naming a token the host never set — only invalidates this variable, and the role still resolves its mapped `--wpds-*` token. Published as the role directly, such a value took the role and every bare `var(<role>)` read site down with it.
|
|
803
|
-
*
|
|
804
|
-
* @param role - The catalog role being overridden.
|
|
805
|
-
* @return The custom property carrying the consumer's value.
|
|
806
|
-
*/
|
|
807
|
-
const themeLayerVar = (role) => `${role}-theme`;
|
|
808
|
-
ROLE_FOR_FIELD.map(([role]) => role);
|
|
809
|
-
/**
|
|
810
|
-
* Whether a value reads the role it is being published for, which would make the catalog entry depend on itself.
|
|
811
|
-
*
|
|
812
|
-
* The dependency runs `<role>: var(<role>-theme, …)`, so a value naming `<role>` closes a cycle through the catalog entry. CSS marks every custom property in a cycle invalid at computed-value time — the role's own fallback is *not* used, so the token resolves to nothing and every chart loses that color. Verified in Chrome: `--role-theme: var(--role, blue); --role: var(--role-theme, green)` leaves `--role` empty, not `green`.
|
|
813
|
-
*
|
|
814
|
-
* Three things this has to get right, none of which a plain substring test gets:
|
|
815
|
-
*
|
|
816
|
-
* `var()` permits whitespace after the opening paren, so testing for `var(<role>` misses `var( --a8c-charts-color-grid, … )`.
|
|
817
|
-
*
|
|
818
|
-
* Several roles are prefixes of others (`--a8c-charts-color-label` of `--a8c-charts-color-label-secondary`), and every role is a prefix of its own theme layer. A prefix match would read a legitimate cross-role pointer as a self-reference and silently drop the override, so the role must be followed by something that cannot continue an identifier — `-` included. A value naming `<role>-theme` is therefore not treated as a self-reference: that cycle is confined to the theme layer, which leaves the role free to fall back to the catalog default.
|
|
819
|
-
*
|
|
820
|
-
* `resolveCssVariable` also accepts a bare custom-property name with no `var()` wrapper (`--a8c-charts-color-grid`), so that is legal `theme` input too. It forms no cycle — custom properties accept arbitrary token streams — so it survives as a literal string and drops silently at the use site instead of falling back to the catalog default.
|
|
821
|
-
*
|
|
822
|
-
* @param value - The consumer's value for the field.
|
|
823
|
-
* @param role - The catalog role this value would override.
|
|
824
|
-
* @return True when the value reads `role`, so it must not be published.
|
|
825
|
-
*/
|
|
826
|
-
const readsOwnRole = (value, role) => value.trim() === role || new RegExp(`var\\(\\s*${role.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w-])`).test(value);
|
|
827
|
-
/**
|
|
828
|
-
* Maps a sparse consumer theme onto the theme-layer variables its overridden catalog roles read, so CSS-painted and JS-resolved colors read one source.
|
|
829
|
-
*
|
|
830
|
-
* A value that reads the role it would override is left unpublished — that is the default theme's own pointer surviving `mergeThemes`, and publishing it would invalidate the role. The role is still reported in `roles`: `withCatalogPointers` restores its theme field to the catalog pointer either way, so CSS and visx agree on the catalog default rather than visx painting a literal CSS never sees.
|
|
831
|
-
*
|
|
832
|
-
* `theme.colors` publishes through the same mechanism, one slot per entry, so a CSS declaration of `--a8c-charts-color-series-N` still beats it and a short array leaves the later slots unset rather than blank.
|
|
833
|
-
*
|
|
834
|
-
* @param theme - The consumer's `theme` prop, before merging with the default theme.
|
|
835
|
-
* @return The wrapper's custom properties, and the roles the consumer overrode.
|
|
836
|
-
*/
|
|
837
|
-
const themeOverrideVars = (theme) => {
|
|
838
|
-
if (!theme) return {
|
|
839
|
-
vars: {},
|
|
840
|
-
roles: []
|
|
841
|
-
};
|
|
842
|
-
if (Array.isArray(theme.colors) && theme.colors.length > 5) warnOnceAboutExtraColors(theme.colors.length);
|
|
843
|
-
const vars = {};
|
|
844
|
-
const roles = [];
|
|
845
|
-
for (const [role, read] of ROLE_FOR_FIELD) {
|
|
846
|
-
const value = read(theme);
|
|
847
|
-
if (typeof value !== "string" || value === "") continue;
|
|
848
|
-
roles.push(role);
|
|
849
|
-
if (!readsOwnRole(value, role)) vars[themeLayerVar(role)] = value;
|
|
850
|
-
}
|
|
851
|
-
return {
|
|
852
|
-
vars,
|
|
853
|
-
roles
|
|
854
|
-
};
|
|
855
|
-
};
|
|
856
|
-
//#endregion
|
|
857
944
|
//#region src/providers/chart-context/themes.ts
|
|
858
945
|
/**
|
|
859
|
-
* Default theme configuration
|
|
946
|
+
* Default theme configuration: the shape and spacing a consumer can override.
|
|
947
|
+
*
|
|
948
|
+
* For a color, set the matching `--a8c-charts-color-*` role in CSS instead; the `var()`
|
|
949
|
+
* chains that deliver those live in `private/catalog-pointers.ts`.
|
|
860
950
|
*/
|
|
861
951
|
const defaultTheme = {
|
|
862
|
-
|
|
863
|
-
labelBackgroundColor: "var(--a8c-charts-color-label-background, transparent)",
|
|
864
|
-
labelTextColor: "var(--a8c-charts-color-label-on-fill, #FFFFFF)",
|
|
865
|
-
colors: [...SERIES_PALETTE_POINTERS],
|
|
866
|
-
gridStyles: {
|
|
867
|
-
stroke: "var(--a8c-charts-color-grid, #dbdbdb)",
|
|
868
|
-
strokeWidth: 1
|
|
869
|
-
},
|
|
952
|
+
gridStyles: { strokeWidth: 1 },
|
|
870
953
|
tickLength: 4,
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
xTickLineStyles: {
|
|
874
|
-
stroke: "var(--a8c-charts-color-tick, #dbdbdb)",
|
|
875
|
-
strokeWidth: 1
|
|
876
|
-
},
|
|
877
|
-
xAxisLineStyles: {
|
|
878
|
-
stroke: "var(--a8c-charts-color-axis, #dbdbdb)",
|
|
879
|
-
strokeWidth: 1
|
|
880
|
-
},
|
|
954
|
+
xTickLineStyles: { strokeWidth: 1 },
|
|
955
|
+
xAxisLineStyles: { strokeWidth: 1 },
|
|
881
956
|
legend: {
|
|
882
|
-
labelStyles: {
|
|
957
|
+
labelStyles: {},
|
|
883
958
|
containerStyles: {},
|
|
884
959
|
shapeStyles: []
|
|
885
960
|
},
|
|
886
961
|
seriesLineStyles: [],
|
|
887
962
|
glyphs: [],
|
|
888
|
-
svgLabelSmall: {
|
|
889
|
-
fill: "var(--a8c-charts-color-label-axis, #1e1e1e)",
|
|
890
|
-
fontFamily: "inherit"
|
|
891
|
-
},
|
|
963
|
+
svgLabelSmall: { fontFamily: "inherit" },
|
|
892
964
|
svgLabelBig: { fontFamily: "inherit" },
|
|
893
|
-
annotationStyles: {
|
|
894
|
-
|
|
895
|
-
anchorLineStroke: "var(--a8c-charts-color-annotation, #1e1e1e)",
|
|
896
|
-
backgroundFill: "var(--a8c-charts-color-surface, #fff)"
|
|
897
|
-
},
|
|
898
|
-
connector: { stroke: "var(--a8c-charts-color-annotation, #1e1e1e)" },
|
|
899
|
-
circleSubject: {
|
|
900
|
-
stroke: "transparent",
|
|
901
|
-
fill: "var(--a8c-charts-color-annotation, #1e1e1e)",
|
|
902
|
-
radius: 5
|
|
903
|
-
}
|
|
904
|
-
},
|
|
905
|
-
geoChart: { featureFillColor: "var(--a8c-charts-color-surface-secondary, #f4f4f4)" },
|
|
906
|
-
leaderboardChart: {
|
|
907
|
-
rowGap: 12,
|
|
908
|
-
columnGap: 4,
|
|
909
|
-
labelSpacing: "xs",
|
|
910
|
-
deltaColors: [
|
|
911
|
-
"var(--a8c-charts-color-trend-down, #cc1818)",
|
|
912
|
-
"var(--a8c-charts-color-trend-neutral, #707070)",
|
|
913
|
-
"var(--a8c-charts-color-trend-up, #008030)"
|
|
914
|
-
]
|
|
915
|
-
},
|
|
916
|
-
conversionFunnelChart: {
|
|
917
|
-
backgroundColor: "var(--a8c-charts-color-surface-secondary, #f4f4f4)",
|
|
918
|
-
positiveChangeColor: "var(--a8c-charts-color-trend-up, #008030)",
|
|
919
|
-
negativeChangeColor: "var(--a8c-charts-color-trend-down, #cc1818)"
|
|
920
|
-
},
|
|
965
|
+
annotationStyles: {},
|
|
966
|
+
leaderboardChart: { labelSpacing: "xs" },
|
|
921
967
|
lineChart: { lineStyles: { comparison: {
|
|
922
968
|
strokeDasharray: "4 4",
|
|
923
969
|
strokeLinecap: "square"
|
|
@@ -941,70 +987,9 @@ const defaultTheme = {
|
|
|
941
987
|
}
|
|
942
988
|
};
|
|
943
989
|
//#endregion
|
|
944
|
-
//#region src/providers/chart-context/private/with-catalog-pointers.ts
|
|
945
|
-
/**
|
|
946
|
-
* The pointer one palette slot is restored to.
|
|
947
|
-
*
|
|
948
|
-
* Unlike the other roles, this keeps the consumer's own color as the pointer's terminal literal instead of the catalog's. The literal is only ever reached where `getComputedStyle` resolves nothing — SSR and jsdom — and there the palette is the one field where falling back to the catalog default would be visible: every series would paint the same seeded blue. In a browser the slot resolves first, so a CSS declaration of it still outranks the consumer's `theme.colors`.
|
|
949
|
-
*
|
|
950
|
-
* @param index - The slot's zero-based index in `colors`.
|
|
951
|
-
* @param value - The merged theme's entry for it.
|
|
952
|
-
* @return The catalog pointer for that slot.
|
|
953
|
-
*/
|
|
954
|
-
const seriesPointer = (index, value) => {
|
|
955
|
-
const role = seriesRole(index + 1);
|
|
956
|
-
if (typeof value === "string" && value.startsWith(`var(${role}`)) return value;
|
|
957
|
-
if (typeof value !== "string" || value === "") return SERIES_PALETTE_POINTERS[index];
|
|
958
|
-
return `var(${role}, ${value})`;
|
|
959
|
-
};
|
|
960
|
-
const CATALOG_RESTORE_FOR_ROLE = {
|
|
961
|
-
...Object.fromEntries(Array.from({ length: 5 }, (_, index) => [seriesRole(index + 1), (theme) => ({ colors: SERIES_PALETTE_POINTERS.map((_pointer, slot) => seriesPointer(slot, theme.colors?.[slot])) })])),
|
|
962
|
-
"--a8c-charts-color-background": () => ({ backgroundColor: defaultTheme.backgroundColor }),
|
|
963
|
-
"--a8c-charts-color-grid": (theme) => ({ gridStyles: {
|
|
964
|
-
...theme.gridStyles,
|
|
965
|
-
stroke: defaultTheme.gridStyles.stroke
|
|
966
|
-
} }),
|
|
967
|
-
"--a8c-charts-color-axis": (theme) => ({ xAxisLineStyles: {
|
|
968
|
-
...theme.xAxisLineStyles,
|
|
969
|
-
stroke: defaultTheme.xAxisLineStyles.stroke
|
|
970
|
-
} }),
|
|
971
|
-
"--a8c-charts-color-tick": (theme) => ({ xTickLineStyles: {
|
|
972
|
-
...theme.xTickLineStyles,
|
|
973
|
-
stroke: defaultTheme.xTickLineStyles.stroke
|
|
974
|
-
} }),
|
|
975
|
-
"--a8c-charts-color-label-axis": (theme) => ({ svgLabelSmall: {
|
|
976
|
-
...theme.svgLabelSmall,
|
|
977
|
-
fill: defaultTheme.svgLabelSmall.fill
|
|
978
|
-
} }),
|
|
979
|
-
"--a8c-charts-color-label-background": () => ({ labelBackgroundColor: defaultTheme.labelBackgroundColor }),
|
|
980
|
-
"--a8c-charts-color-label-on-fill": () => ({ labelTextColor: defaultTheme.labelTextColor })
|
|
981
|
-
};
|
|
982
|
-
/**
|
|
983
|
-
* Restores the mapped theme fields of `overriddenRoles` to their catalog pointer, so the theme-layer variable `themeOverrideVars` writes on the provider wrapper is the only carrier for an overridden role — CSS and the JS bridge (`useXYChartTheme`) then resolve it through the same cascade instead of visx reading a baked literal that can disagree with a closer CSS override.
|
|
984
|
-
*
|
|
985
|
-
* This covers roles `themeOverrideVars` deliberately left unpublished as well as the ones it published. A value that reads its own role is not publishable, but leaving visx the consumer's literal is what the whole mechanism exists to prevent: `theme={ { gridStyles: { stroke: 'var(--brand, var(--a8c-charts-color-grid, red))' } } }` would have CSS paint the catalog default while visx painted `--brand`.
|
|
986
|
-
*
|
|
987
|
-
* Every field outside the mapped roles is left exactly as `merged` provided it: those aren't published as theme-layer vars, so rewriting them would erase a consumer's override with no replacement carrier.
|
|
988
|
-
*
|
|
989
|
-
* @param merged - The consumer theme merged onto `defaultTheme` (`mergeThemes` output).
|
|
990
|
-
* @param overriddenRoles - The catalog roles the consumer overrode, from `themeOverrideVars`.
|
|
991
|
-
* @return A new theme object; `merged` is not mutated.
|
|
992
|
-
*/
|
|
993
|
-
const withCatalogPointers = (merged, overriddenRoles) => {
|
|
994
|
-
let result = merged;
|
|
995
|
-
for (const role of overriddenRoles) {
|
|
996
|
-
const restore = CATALOG_RESTORE_FOR_ROLE[role];
|
|
997
|
-
if (restore) result = {
|
|
998
|
-
...result,
|
|
999
|
-
...restore(result)
|
|
1000
|
-
};
|
|
1001
|
-
}
|
|
1002
|
-
return result;
|
|
1003
|
-
};
|
|
1004
|
-
//#endregion
|
|
1005
990
|
//#region src/providers/chart-context/global-charts-provider.tsx
|
|
1006
991
|
const GlobalChartsContext = (0, react.createContext)(null);
|
|
1007
|
-
const GlobalChartsProvider = ({ children, theme }) => {
|
|
992
|
+
const GlobalChartsProvider = ({ children, theme, locale, timeZone }) => {
|
|
1008
993
|
const [charts, setCharts] = (0, react.useState)(() => /* @__PURE__ */ new Map());
|
|
1009
994
|
const [hiddenSeries, setHiddenSeries] = (0, react.useState)(() => /* @__PURE__ */ new Map());
|
|
1010
995
|
const seededCharts = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
@@ -1014,11 +999,7 @@ const GlobalChartsProvider = ({ children, theme }) => {
|
|
|
1014
999
|
wrapperRef.current = node;
|
|
1015
1000
|
setScopeNode(node);
|
|
1016
1001
|
}, []);
|
|
1017
|
-
const
|
|
1018
|
-
const providerTheme = (0, react.useMemo)(() => {
|
|
1019
|
-
if (!theme) return defaultTheme;
|
|
1020
|
-
return withCatalogPointers(mergeThemes(defaultTheme, theme), overriddenRoles);
|
|
1021
|
-
}, [theme, overriddenRoles]);
|
|
1002
|
+
const providerTheme = (0, react.useMemo)(() => theme ? mergeThemes(defaultTheme, theme) : defaultTheme, [theme]);
|
|
1022
1003
|
const [colorCache, setColorCache] = (0, react.useState)(() => ({
|
|
1023
1004
|
colors: [],
|
|
1024
1005
|
hues: [],
|
|
@@ -1034,7 +1015,7 @@ const GlobalChartsProvider = ({ children, theme }) => {
|
|
|
1034
1015
|
const existingHslColors = [];
|
|
1035
1016
|
let minHue = 360;
|
|
1036
1017
|
let maxHue = 0;
|
|
1037
|
-
for (const color of
|
|
1018
|
+
for (const color of SERIES_PALETTE_POINTERS) {
|
|
1038
1019
|
const normalizedColor = normalizeColorToHex(color, wrapperRef.current, resolveCssVariable);
|
|
1039
1020
|
if (normalizedColor.startsWith("#")) {
|
|
1040
1021
|
resolvedColors.push(normalizedColor);
|
|
@@ -1059,7 +1040,7 @@ const GlobalChartsProvider = ({ children, theme }) => {
|
|
|
1059
1040
|
minHue,
|
|
1060
1041
|
maxHue
|
|
1061
1042
|
});
|
|
1062
|
-
}, [
|
|
1043
|
+
}, []);
|
|
1063
1044
|
(0, react.useEffect)(() => {
|
|
1064
1045
|
if (colorCache.colors.length > 0) setIsColorPaletteResolved(true);
|
|
1065
1046
|
}, [colorCache]);
|
|
@@ -1151,12 +1132,17 @@ const GlobalChartsProvider = ({ children, theme }) => {
|
|
|
1151
1132
|
const set = hiddenSeries.get(chartId);
|
|
1152
1133
|
return set ? new Set(set) : /* @__PURE__ */ new Set();
|
|
1153
1134
|
}, [hiddenSeries]);
|
|
1135
|
+
const formatting = (0, react.useMemo)(() => sanitizeFormatting({
|
|
1136
|
+
locale,
|
|
1137
|
+
timeZone
|
|
1138
|
+
}), [locale, timeZone]);
|
|
1154
1139
|
const value = (0, react.useMemo)(() => ({
|
|
1155
1140
|
charts,
|
|
1156
1141
|
registerChart,
|
|
1157
1142
|
unregisterChart,
|
|
1158
1143
|
getChartData,
|
|
1159
1144
|
theme: providerTheme,
|
|
1145
|
+
formatting,
|
|
1160
1146
|
getElementStyles,
|
|
1161
1147
|
toggleSeriesVisibility,
|
|
1162
1148
|
setSeriesVisibility,
|
|
@@ -1172,6 +1158,7 @@ const GlobalChartsProvider = ({ children, theme }) => {
|
|
|
1172
1158
|
unregisterChart,
|
|
1173
1159
|
getChartData,
|
|
1174
1160
|
providerTheme,
|
|
1161
|
+
formatting,
|
|
1175
1162
|
getElementStyles,
|
|
1176
1163
|
toggleSeriesVisibility,
|
|
1177
1164
|
setSeriesVisibility,
|
|
@@ -1187,10 +1174,7 @@ const GlobalChartsProvider = ({ children, theme }) => {
|
|
|
1187
1174
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1188
1175
|
ref: setWrapperNode,
|
|
1189
1176
|
className: CHART_SCOPE_CLASS,
|
|
1190
|
-
style: {
|
|
1191
|
-
display: "contents",
|
|
1192
|
-
...overrideVars
|
|
1193
|
-
},
|
|
1177
|
+
style: { display: "contents" },
|
|
1194
1178
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartScopeContext.Provider, {
|
|
1195
1179
|
value: scopeNode,
|
|
1196
1180
|
children
|
|
@@ -1254,6 +1238,39 @@ const useDeepMemo = (value) => {
|
|
|
1254
1238
|
return ref.current;
|
|
1255
1239
|
};
|
|
1256
1240
|
//#endregion
|
|
1241
|
+
//#region src/providers/chart-context/private/catalog-pointers.ts
|
|
1242
|
+
const CATALOG_POINTERS = {
|
|
1243
|
+
background: "var(--a8c-charts-color-background, #fff)",
|
|
1244
|
+
labelAxis: "var(--a8c-charts-color-label-axis, #1e1e1e)",
|
|
1245
|
+
grid: "var(--a8c-charts-color-grid, #dbdbdb)",
|
|
1246
|
+
axisX: "var(--a8c-charts-color-axis-x, #dbdbdb)",
|
|
1247
|
+
tickX: "var(--a8c-charts-color-tick-x, #dbdbdb)",
|
|
1248
|
+
axisY: "var(--a8c-charts-color-axis-y, none)",
|
|
1249
|
+
tickY: "var(--a8c-charts-color-tick-y, none)",
|
|
1250
|
+
annotation: "var(--a8c-charts-color-annotation, #1e1e1e)",
|
|
1251
|
+
surface: "var(--a8c-charts-color-surface, #fff)",
|
|
1252
|
+
surfaceSecondary: "var(--a8c-charts-color-surface-secondary, #f4f4f4)",
|
|
1253
|
+
trendUp: "var(--a8c-charts-color-trend-up, #008030)",
|
|
1254
|
+
trendDown: "var(--a8c-charts-color-trend-down, #cc1818)",
|
|
1255
|
+
series: SERIES_PALETTE_POINTERS
|
|
1256
|
+
};
|
|
1257
|
+
/**
|
|
1258
|
+
* The annotation parts visx paints, in the shape `@visx/annotation` takes. `radius`
|
|
1259
|
+
* rides along as the base the theme and the per-datum styles merge onto.
|
|
1260
|
+
*/
|
|
1261
|
+
const ANNOTATION_POINTERS = {
|
|
1262
|
+
label: {
|
|
1263
|
+
anchorLineStroke: CATALOG_POINTERS.annotation,
|
|
1264
|
+
backgroundFill: CATALOG_POINTERS.surface
|
|
1265
|
+
},
|
|
1266
|
+
connector: { stroke: CATALOG_POINTERS.annotation },
|
|
1267
|
+
circleSubject: {
|
|
1268
|
+
stroke: "transparent",
|
|
1269
|
+
fill: CATALOG_POINTERS.annotation,
|
|
1270
|
+
radius: 5
|
|
1271
|
+
}
|
|
1272
|
+
};
|
|
1273
|
+
//#endregion
|
|
1257
1274
|
//#region src/providers/chart-scope/use-chart-scope-element.ts
|
|
1258
1275
|
/**
|
|
1259
1276
|
* Returns the nearest chart scope element, or `null` when rendered outside any scope (and during SSR, before the ref is attached).
|
|
@@ -1280,13 +1297,34 @@ const useXYChartTheme = (data) => {
|
|
|
1280
1297
|
return (0, react.useMemo)(() => {
|
|
1281
1298
|
const resolve = createCssVariableResolver(scopeElement);
|
|
1282
1299
|
const resolveColor = (value) => value ? resolve(value) ?? value : value;
|
|
1283
|
-
const paletteColors = [...JSON.parse(seriesColorKey), ...
|
|
1284
|
-
const
|
|
1300
|
+
const paletteColors = [...JSON.parse(seriesColorKey), ...CATALOG_POINTERS.series].map((color) => resolveColor(color)).filter((color) => Boolean(color) && !color.includes("var("));
|
|
1301
|
+
const resolvedLabelColor = resolveColor(CATALOG_POINTERS.labelAxis);
|
|
1302
|
+
const htmlLabelColor = resolvedLabelColor ? normalizeColorToHex(resolvedLabelColor) || resolvedLabelColor : resolvedLabelColor;
|
|
1285
1303
|
return (0, _visx_xychart.buildChartTheme)({
|
|
1286
1304
|
...theme,
|
|
1305
|
+
gridColor: "",
|
|
1306
|
+
gridColorDark: "",
|
|
1287
1307
|
colors: paletteColors,
|
|
1288
|
-
backgroundColor: resolveColor(
|
|
1289
|
-
htmlLabel: htmlLabelColor ? { color: htmlLabelColor } : void 0
|
|
1308
|
+
backgroundColor: resolveColor(CATALOG_POINTERS.background),
|
|
1309
|
+
htmlLabel: htmlLabelColor ? { color: htmlLabelColor } : void 0,
|
|
1310
|
+
gridStyles: {
|
|
1311
|
+
...theme.gridStyles,
|
|
1312
|
+
stroke: CATALOG_POINTERS.grid
|
|
1313
|
+
},
|
|
1314
|
+
xAxisLineStyles: {
|
|
1315
|
+
...theme.xAxisLineStyles,
|
|
1316
|
+
stroke: CATALOG_POINTERS.axisX
|
|
1317
|
+
},
|
|
1318
|
+
xTickLineStyles: {
|
|
1319
|
+
...theme.xTickLineStyles,
|
|
1320
|
+
stroke: CATALOG_POINTERS.tickX
|
|
1321
|
+
},
|
|
1322
|
+
yAxisLineStyles: { stroke: CATALOG_POINTERS.axisY },
|
|
1323
|
+
yTickLineStyles: { stroke: CATALOG_POINTERS.tickY },
|
|
1324
|
+
svgLabelSmall: {
|
|
1325
|
+
...theme.svgLabelSmall,
|
|
1326
|
+
fill: CATALOG_POINTERS.labelAxis
|
|
1327
|
+
}
|
|
1290
1328
|
});
|
|
1291
1329
|
}, [
|
|
1292
1330
|
theme,
|
|
@@ -1295,13 +1333,28 @@ const useXYChartTheme = (data) => {
|
|
|
1295
1333
|
]);
|
|
1296
1334
|
};
|
|
1297
1335
|
//#endregion
|
|
1336
|
+
//#region src/providers/chart-context/hooks/use-chart-formatting.ts
|
|
1337
|
+
const RUNTIME_FORMATTING = {};
|
|
1338
|
+
/**
|
|
1339
|
+
* The locale and time zone dates are rendered in, as set on `GlobalChartsProvider`.
|
|
1340
|
+
*
|
|
1341
|
+
* Falls back to the runtime's own rather than throwing: charts render outside a
|
|
1342
|
+
* provider, and a host that sets neither gets exactly the browser-default
|
|
1343
|
+
* behavior this package had before the context existed.
|
|
1344
|
+
*
|
|
1345
|
+
* @return The host's formatting context.
|
|
1346
|
+
*/
|
|
1347
|
+
const useChartFormatting = () => {
|
|
1348
|
+
return (0, react.useContext)(GlobalChartsContext)?.formatting ?? RUNTIME_FORMATTING;
|
|
1349
|
+
};
|
|
1350
|
+
//#endregion
|
|
1298
1351
|
//#region src/hooks/use-chart-data-transform.ts
|
|
1299
1352
|
/**
|
|
1300
1353
|
* Hook that transforms and sorts chart data, handling date parsing and sorting
|
|
1301
1354
|
*
|
|
1302
1355
|
* This hook extracts the common data transformation logic used in both line-chart
|
|
1303
1356
|
* and bar-chart components. It:
|
|
1304
|
-
* 1. Parses date strings into Date objects
|
|
1357
|
+
* 1. Parses date strings into Date objects, dated in the provider's time zone
|
|
1305
1358
|
* 2. Sorts data points by date when date properties are present
|
|
1306
1359
|
* 3. Returns the original data unchanged when no date properties are found
|
|
1307
1360
|
*
|
|
@@ -1309,6 +1362,7 @@ const useXYChartTheme = (data) => {
|
|
|
1309
1362
|
* @return {SeriesData[]} The transformed and sorted data
|
|
1310
1363
|
*/
|
|
1311
1364
|
const useChartDataTransform = (data) => {
|
|
1365
|
+
const { timeZone } = useChartFormatting();
|
|
1312
1366
|
return (0, react.useMemo)(() => {
|
|
1313
1367
|
const firstPoint = data?.[0]?.data?.[0];
|
|
1314
1368
|
if (!(firstPoint && ("date" in firstPoint || "dateString" in firstPoint))) return data;
|
|
@@ -1317,7 +1371,7 @@ const useChartDataTransform = (data) => {
|
|
|
1317
1371
|
data: series.data.map((point) => {
|
|
1318
1372
|
let date;
|
|
1319
1373
|
if ("date" in point && point.date) date = point.date;
|
|
1320
|
-
else if ("dateString" in point && point.dateString) date = parseAsLocalDate(point.dateString);
|
|
1374
|
+
else if ("dateString" in point && point.dateString) date = parseAsLocalDate(point.dateString, timeZone);
|
|
1321
1375
|
return {
|
|
1322
1376
|
...point,
|
|
1323
1377
|
date
|
|
@@ -1327,7 +1381,7 @@ const useChartDataTransform = (data) => {
|
|
|
1327
1381
|
return a.date.getTime() - b.date.getTime();
|
|
1328
1382
|
})
|
|
1329
1383
|
}));
|
|
1330
|
-
}, [data]);
|
|
1384
|
+
}, [data, timeZone]);
|
|
1331
1385
|
};
|
|
1332
1386
|
//#endregion
|
|
1333
1387
|
//#region src/hooks/use-chart-margin.tsx
|
|
@@ -1369,11 +1423,32 @@ const DEFAULT_TICK_LENGTH = 8;
|
|
|
1369
1423
|
* measure them via getLongestTickWidth.
|
|
1370
1424
|
*/
|
|
1371
1425
|
const DEFAULT_Y_TICK_WIDTH = 40;
|
|
1426
|
+
/**
|
|
1427
|
+
* Copy a label style with its lengths spelled out in px.
|
|
1428
|
+
*
|
|
1429
|
+
* `getStringWidth` applies the style through CSSOM. Blink resolves a bare number
|
|
1430
|
+
* on an SVG `<text>`, but that is its own leniency rather than the CSS rule, and
|
|
1431
|
+
* `buildChartTheme` hands us bare numbers.
|
|
1432
|
+
*
|
|
1433
|
+
* @param style - Raw label style from the theme.
|
|
1434
|
+
* @return The same style with px-qualified lengths.
|
|
1435
|
+
*/
|
|
1436
|
+
const toMeasurableStyle = (style) => {
|
|
1437
|
+
if (!style) return style;
|
|
1438
|
+
const fontSize = resolveFontSize(style.fontSize);
|
|
1439
|
+
const { letterSpacing } = style;
|
|
1440
|
+
return {
|
|
1441
|
+
...style,
|
|
1442
|
+
...fontSize === void 0 ? {} : { fontSize: `${fontSize}px` },
|
|
1443
|
+
...typeof letterSpacing === "number" ? { letterSpacing: `${letterSpacing}px` } : {}
|
|
1444
|
+
};
|
|
1445
|
+
};
|
|
1372
1446
|
const getXAxisLabelMetrics = (theme, orientation) => {
|
|
1373
1447
|
const xAxisStyles = orientation === "top" ? theme.axisStyles?.x?.top : theme.axisStyles?.x?.bottom;
|
|
1374
1448
|
return {
|
|
1375
1449
|
fontSize: resolveFontSize(xAxisStyles?.axisLabel?.fontSize) || resolveFontSize(theme.svgLabelSmall?.fontSize) || DEFAULT_FONT_SIZE,
|
|
1376
|
-
tickLength: xAxisStyles?.tickLength ?? DEFAULT_TICK_LENGTH
|
|
1450
|
+
tickLength: xAxisStyles?.tickLength ?? DEFAULT_TICK_LENGTH,
|
|
1451
|
+
tickLabelStyle: toMeasurableStyle(xAxisStyles?.tickLabel)
|
|
1377
1452
|
};
|
|
1378
1453
|
};
|
|
1379
1454
|
const useChartMargin = (height, options, data, theme, horizontal = false) => {
|
|
@@ -1385,7 +1460,7 @@ const useChartMargin = (height, options, data, theme, horizontal = false) => {
|
|
|
1385
1460
|
const maxY = Math.max(...allDataPoints.map((d) => d.value));
|
|
1386
1461
|
const yScale = (0, _visx_scale.createScale)({
|
|
1387
1462
|
...options.yScale,
|
|
1388
|
-
domain: [minY, maxY],
|
|
1463
|
+
domain: options.yScale?.domain ?? [minY, maxY],
|
|
1389
1464
|
range: [height, 0]
|
|
1390
1465
|
});
|
|
1391
1466
|
return (0, _visx_scale.getTicks)(yScale, options.axis?.y?.numTicks);
|
|
@@ -1404,18 +1479,25 @@ const useChartMargin = (height, options, data, theme, horizontal = false) => {
|
|
|
1404
1479
|
};
|
|
1405
1480
|
const yAxisOrientation = options.axis?.y?.orientation;
|
|
1406
1481
|
const yAxisStyles = yAxisOrientation === "right" ? theme.axisStyles.y.right : theme.axisStyles.y.left;
|
|
1407
|
-
const yTickWidth = getLongestTickWidth(yTicks, options.axis?.y?.tickFormat, yAxisStyles.axisLabel);
|
|
1482
|
+
const yTickWidth = getLongestTickWidth(yTicks, options.axis?.y?.tickFormat, toMeasurableStyle(yAxisStyles.axisLabel));
|
|
1408
1483
|
const yTickLabelFontSize = resolveFontSize(yAxisStyles?.tickLabel?.fontSize) || DEFAULT_FONT_SIZE;
|
|
1409
1484
|
const yMarginValue = (yTickWidth ?? DEFAULT_Y_TICK_WIDTH) + (yAxisStyles?.tickLength ?? 0) + Math.ceil(yTickLabelFontSize * .25);
|
|
1410
|
-
if (
|
|
1411
|
-
|
|
1485
|
+
if (options.axis?.y?.display !== false) {
|
|
1486
|
+
if (yAxisOrientation === "right") defaultMargin.right = yMarginValue;
|
|
1487
|
+
else defaultMargin.left = yMarginValue;
|
|
1488
|
+
}
|
|
1412
1489
|
const xOrientation = options.axis?.x?.orientation === "top" ? "top" : "bottom";
|
|
1413
|
-
const { fontSize, tickLength } = getXAxisLabelMetrics(theme, xOrientation);
|
|
1490
|
+
const { fontSize, tickLength, tickLabelStyle } = getXAxisLabelMetrics(theme, xOrientation);
|
|
1414
1491
|
const computedXMargin = fontSize + tickLength;
|
|
1415
1492
|
if (xOrientation === "top") {
|
|
1416
1493
|
defaultMargin.top = Math.max(defaultMargin.top, computedXMargin);
|
|
1417
1494
|
defaultMargin.bottom = DEFAULT_BOTTOM_FOR_TOP_AXIS;
|
|
1418
1495
|
} else defaultMargin.bottom = Math.max(defaultMargin.bottom, computedXMargin);
|
|
1496
|
+
if (options.axis?.x?.display !== false) {
|
|
1497
|
+
const { first, last } = getEdgeTickWidths(options.axis?.x?.tickValues ?? [], options.axis?.x?.tickFormat, tickLabelStyle);
|
|
1498
|
+
defaultMargin.left = Math.max(defaultMargin.left, Math.ceil(first / 2));
|
|
1499
|
+
defaultMargin.right = Math.max(defaultMargin.right, Math.ceil(last / 2));
|
|
1500
|
+
}
|
|
1419
1501
|
return defaultMargin;
|
|
1420
1502
|
}, [
|
|
1421
1503
|
options,
|
|
@@ -2128,15 +2210,370 @@ const BaseTooltip = ({ data, top, left, component: Component = DefaultTooltipCon
|
|
|
2128
2210
|
});
|
|
2129
2211
|
};
|
|
2130
2212
|
//#endregion
|
|
2213
|
+
//#region src/components/tooltip/private/bounded-tooltip.tsx
|
|
2214
|
+
const DEFAULT_OFFSET = 10;
|
|
2215
|
+
const POINTER_HEIGHT = 6;
|
|
2216
|
+
const clamp = (position, min, max, size) => Math.min(Math.max(position, min), Math.max(min, max - size));
|
|
2217
|
+
const isClipping = (element) => {
|
|
2218
|
+
const { overflow, overflowX, overflowY } = getComputedStyle(element);
|
|
2219
|
+
return [
|
|
2220
|
+
overflow,
|
|
2221
|
+
overflowX,
|
|
2222
|
+
overflowY
|
|
2223
|
+
].some((value) => value && value !== "visible");
|
|
2224
|
+
};
|
|
2225
|
+
const findClippingAncestor = (wrapper) => {
|
|
2226
|
+
let element = wrapper.parentElement;
|
|
2227
|
+
while (element && element !== document.body) {
|
|
2228
|
+
if (isClipping(element)) return element;
|
|
2229
|
+
element = element.parentElement;
|
|
2230
|
+
}
|
|
2231
|
+
return null;
|
|
2232
|
+
};
|
|
2233
|
+
/**
|
|
2234
|
+
* Where the box goes, in wrapper coordinates. Automatic placement flips to
|
|
2235
|
+
* the side that clips less, then clamps inside `bounds`; below-axis placement
|
|
2236
|
+
* is centered on the anchor and clamped horizontally.
|
|
2237
|
+
*
|
|
2238
|
+
* The flip measures against the visible part of the wrapper, its intersection
|
|
2239
|
+
* with `bounds`. Only the clamp is bound by the page: a box that fits inside the
|
|
2240
|
+
* chart on one side stays there, so it does not reach past the chart into
|
|
2241
|
+
* siblings that paint over it.
|
|
2242
|
+
*
|
|
2243
|
+
* @param params - Anchor, offsets, the box size and the rects to keep inside.
|
|
2244
|
+
* @param params.left - Anchor x, in wrapper coordinates.
|
|
2245
|
+
* @param params.top - Anchor y, in wrapper coordinates.
|
|
2246
|
+
* @param params.offsetLeft - Gap between the anchor and the box, horizontally.
|
|
2247
|
+
* @param params.offsetTop - Gap between the anchor and the box, vertically.
|
|
2248
|
+
* @param params.box - Rendered size of the box.
|
|
2249
|
+
* @param params.wrapper - The wrapper's own edges, in wrapper coordinates.
|
|
2250
|
+
* @param params.placement - Fixed below-axis placement or automatic flipping.
|
|
2251
|
+
* @param params.bounds - Edges the box must keep inside, in wrapper coordinates.
|
|
2252
|
+
* @return The box's top-left corner; fixed placement preserves the axis's subpixel y.
|
|
2253
|
+
*/
|
|
2254
|
+
const getBoundedPosition = ({ left, top, offsetLeft, offsetTop, box, wrapper, bounds, placement = "auto" }) => {
|
|
2255
|
+
if (placement === "below-axis") return {
|
|
2256
|
+
x: Math.round(clamp(left - box.width / 2, bounds.left, bounds.right, box.width)),
|
|
2257
|
+
y: top + POINTER_HEIGHT
|
|
2258
|
+
};
|
|
2259
|
+
const fit = {
|
|
2260
|
+
left: Math.max(wrapper.left, bounds.left),
|
|
2261
|
+
top: Math.max(wrapper.top, bounds.top),
|
|
2262
|
+
right: Math.min(wrapper.right, bounds.right),
|
|
2263
|
+
bottom: Math.min(wrapper.bottom, bounds.bottom)
|
|
2264
|
+
};
|
|
2265
|
+
const rightX = left + offsetLeft;
|
|
2266
|
+
const leftX = left - offsetLeft - box.width;
|
|
2267
|
+
const rightOverflow = rightX + box.width - fit.right;
|
|
2268
|
+
const leftOverflow = fit.left - leftX;
|
|
2269
|
+
let x = rightOverflow > 0 && rightOverflow > leftOverflow ? leftX : rightX;
|
|
2270
|
+
const downY = top + offsetTop;
|
|
2271
|
+
const upY = top - offsetTop - box.height;
|
|
2272
|
+
const downOverflow = downY + box.height - fit.bottom;
|
|
2273
|
+
const upOverflow = fit.top - upY;
|
|
2274
|
+
let y = downOverflow > 0 && downOverflow > upOverflow ? upY : downY;
|
|
2275
|
+
x = clamp(x, bounds.left, bounds.right, box.width);
|
|
2276
|
+
y = clamp(y, bounds.top, bounds.bottom, box.height);
|
|
2277
|
+
return {
|
|
2278
|
+
x: Math.round(x),
|
|
2279
|
+
y: Math.round(y)
|
|
2280
|
+
};
|
|
2281
|
+
};
|
|
2282
|
+
/**
|
|
2283
|
+
* visx's `Tooltip`, positioned like its `TooltipWithBounds` but kept inside the
|
|
2284
|
+
* nearest clipping ancestor — or the viewport when there is none — rather than
|
|
2285
|
+
* inside its own parent. Rendered in-tree, a tooltip's parent is the chart
|
|
2286
|
+
* wrapper, which is often narrower than the box; measuring against the parent
|
|
2287
|
+
* alone would let the box spill into an `overflow: hidden` card and be cut off.
|
|
2288
|
+
*
|
|
2289
|
+
* Re-measures on every render, so a box whose content changes width between
|
|
2290
|
+
* two hovers is placed for its current size. See `getBoundedPosition` for the
|
|
2291
|
+
* below-axis exception.
|
|
2292
|
+
*
|
|
2293
|
+
* @param props - visx `Tooltip` props.
|
|
2294
|
+
* @param props.left - Anchor x, in wrapper coordinates.
|
|
2295
|
+
* @param props.top - Anchor y, in wrapper coordinates.
|
|
2296
|
+
* @param props.offsetLeft - Gap between the anchor and the box, horizontally.
|
|
2297
|
+
* @param props.offsetTop - Gap between the anchor and the box, vertically.
|
|
2298
|
+
* @param props.style - Box styles; visx's defaults unless `unstyled`.
|
|
2299
|
+
* @param props.unstyled - Skip `style` and leave the box bare.
|
|
2300
|
+
* @param props.children - Box content.
|
|
2301
|
+
* @param props.placement - Fixed below-axis placement or automatic flipping.
|
|
2302
|
+
* @return The tooltip box.
|
|
2303
|
+
*/
|
|
2304
|
+
const BoundedTooltip = ({ left = 0, top = 0, offsetLeft = DEFAULT_OFFSET, offsetTop = DEFAULT_OFFSET, style = _visx_tooltip.defaultStyles, unstyled = false, children, placement = "auto", ...rest }) => {
|
|
2305
|
+
const nodeRef = (0, react.useRef)(null);
|
|
2306
|
+
const clipRef = (0, react.useRef)(null);
|
|
2307
|
+
const [position, setPosition] = (0, react.useState)(null);
|
|
2308
|
+
(0, react.useLayoutEffect)(() => {
|
|
2309
|
+
const node = nodeRef.current;
|
|
2310
|
+
const wrapper = node?.parentElement;
|
|
2311
|
+
if (!node || !wrapper) return;
|
|
2312
|
+
if (clipRef.current?.wrapper !== wrapper) clipRef.current = {
|
|
2313
|
+
wrapper,
|
|
2314
|
+
clip: findClippingAncestor(wrapper)
|
|
2315
|
+
};
|
|
2316
|
+
const own = node.getBoundingClientRect();
|
|
2317
|
+
const wrapperRect = wrapper.getBoundingClientRect();
|
|
2318
|
+
const clipRect = clipRef.current.clip?.getBoundingClientRect() ?? {
|
|
2319
|
+
left: 0,
|
|
2320
|
+
top: 0,
|
|
2321
|
+
right: window.innerWidth,
|
|
2322
|
+
bottom: window.innerHeight
|
|
2323
|
+
};
|
|
2324
|
+
const next = getBoundedPosition({
|
|
2325
|
+
left,
|
|
2326
|
+
top,
|
|
2327
|
+
offsetLeft,
|
|
2328
|
+
offsetTop,
|
|
2329
|
+
placement,
|
|
2330
|
+
box: {
|
|
2331
|
+
width: own.width,
|
|
2332
|
+
height: own.height
|
|
2333
|
+
},
|
|
2334
|
+
wrapper: {
|
|
2335
|
+
left: 0,
|
|
2336
|
+
top: 0,
|
|
2337
|
+
right: wrapperRect.width,
|
|
2338
|
+
bottom: wrapperRect.height
|
|
2339
|
+
},
|
|
2340
|
+
bounds: {
|
|
2341
|
+
left: clipRect.left - wrapperRect.left,
|
|
2342
|
+
top: clipRect.top - wrapperRect.top,
|
|
2343
|
+
right: clipRect.right - wrapperRect.left,
|
|
2344
|
+
bottom: clipRect.bottom - wrapperRect.top
|
|
2345
|
+
}
|
|
2346
|
+
});
|
|
2347
|
+
setPosition((current) => current && current.x === next.x && current.y === next.y ? current : next);
|
|
2348
|
+
}, [
|
|
2349
|
+
left,
|
|
2350
|
+
top,
|
|
2351
|
+
offsetLeft,
|
|
2352
|
+
offsetTop,
|
|
2353
|
+
children,
|
|
2354
|
+
placement
|
|
2355
|
+
]);
|
|
2356
|
+
const x = position?.x ?? left + offsetLeft;
|
|
2357
|
+
const y = position?.y ?? top + (placement === "below-axis" ? POINTER_HEIGHT : offsetTop);
|
|
2358
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_visx_tooltip.Tooltip, {
|
|
2359
|
+
ref: nodeRef,
|
|
2360
|
+
style: {
|
|
2361
|
+
position: "absolute",
|
|
2362
|
+
left: 0,
|
|
2363
|
+
top: 0,
|
|
2364
|
+
transform: `translate(${x}px, ${y}px)`,
|
|
2365
|
+
...!unstyled && style
|
|
2366
|
+
},
|
|
2367
|
+
...rest,
|
|
2368
|
+
children: [placement === "below-axis" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2369
|
+
"aria-hidden": "true",
|
|
2370
|
+
style: {
|
|
2371
|
+
position: "absolute",
|
|
2372
|
+
left: left - x - POINTER_HEIGHT,
|
|
2373
|
+
top: -6,
|
|
2374
|
+
width: 12,
|
|
2375
|
+
height: POINTER_HEIGHT,
|
|
2376
|
+
background: "inherit",
|
|
2377
|
+
clipPath: "polygon(50% 0, 100% 100%, 0 100%)",
|
|
2378
|
+
pointerEvents: "none"
|
|
2379
|
+
}
|
|
2380
|
+
}), children]
|
|
2381
|
+
});
|
|
2382
|
+
};
|
|
2383
|
+
//#endregion
|
|
2384
|
+
//#region src/components/tooltip/xy-chart-tooltip.tsx
|
|
2385
|
+
const CROSSHAIR_STROKE_WIDTH = 1.5;
|
|
2386
|
+
const CROSSHAIR_PAINT_PROPERTIES = /* @__PURE__ */ new Set([
|
|
2387
|
+
"stroke",
|
|
2388
|
+
"strokeWidth",
|
|
2389
|
+
"strokeOpacity",
|
|
2390
|
+
"strokeDasharray",
|
|
2391
|
+
"strokeLinecap",
|
|
2392
|
+
"opacity"
|
|
2393
|
+
]);
|
|
2394
|
+
const crosshairPaintProps = (props = {}) => {
|
|
2395
|
+
const pickPaint = (value) => Object.fromEntries(Object.entries(value).filter(([key]) => CROSSHAIR_PAINT_PROPERTIES.has(key)));
|
|
2396
|
+
return {
|
|
2397
|
+
...pickPaint(props),
|
|
2398
|
+
...props.className === void 0 ? {} : { className: props.className },
|
|
2399
|
+
...props.style === void 0 ? {} : { style: pickPaint(props.style) }
|
|
2400
|
+
};
|
|
2401
|
+
};
|
|
2402
|
+
const DEFAULT_GLYPH_RADIUS = 4;
|
|
2403
|
+
const FALLBACK_COLOR = "#222";
|
|
2404
|
+
const DEFAULT_TOOLTIP_Z_INDEX = 3;
|
|
2405
|
+
const PORTAL_OPTIONS = /* @__PURE__ */ new Set([
|
|
2406
|
+
"scroll",
|
|
2407
|
+
"debounce",
|
|
2408
|
+
"resizeObserverPolyfill"
|
|
2409
|
+
]);
|
|
2410
|
+
const isValidNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
2411
|
+
const scaleBandwidth = (scale) => {
|
|
2412
|
+
const bandwidth = scale?.bandwidth;
|
|
2413
|
+
return typeof bandwidth === "function" ? Number(bandwidth()) : 0;
|
|
2414
|
+
};
|
|
2415
|
+
const DefaultGlyph$1 = ({ x, y, size, color, glyphStyle, seriesKey }) => {
|
|
2416
|
+
const { theme } = (0, react.useContext)(_visx_xychart.DataContext) || {};
|
|
2417
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
2418
|
+
cx: x,
|
|
2419
|
+
cy: y,
|
|
2420
|
+
r: size,
|
|
2421
|
+
fill: color,
|
|
2422
|
+
stroke: theme?.backgroundColor,
|
|
2423
|
+
strokeWidth: CROSSHAIR_STROKE_WIDTH,
|
|
2424
|
+
paintOrder: "fill",
|
|
2425
|
+
...glyphStyle
|
|
2426
|
+
});
|
|
2427
|
+
};
|
|
2428
|
+
const defaultRenderGlyph$1 = ({ key, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultGlyph$1, {
|
|
2429
|
+
seriesKey: key,
|
|
2430
|
+
...props
|
|
2431
|
+
}, key);
|
|
2432
|
+
const XyChartTooltipContent = ({ tooltipContext, container, renderTooltip, renderGlyph = defaultRenderGlyph$1, glyphStyle, snapTooltipToDatumX = false, snapTooltipToDatumY = false, showVerticalCrosshair = false, showHorizontalCrosshair = false, showDatumGlyph = false, showSeriesGlyphs = false, verticalCrosshairStyle, horizontalCrosshairStyle, detectBounds = true, tooltipPlacement = "auto", zIndex = DEFAULT_TOOLTIP_Z_INDEX, style, ...rest }) => {
|
|
2433
|
+
const tooltipProps = Object.fromEntries(Object.entries(rest).filter(([key]) => !PORTAL_OPTIONS.has(key)));
|
|
2434
|
+
const { colorScale, theme, innerHeight = 0, innerWidth = 0, margin, xScale, yScale, dataRegistry } = (0, react.useContext)(_visx_xychart.DataContext) || {};
|
|
2435
|
+
const tooltipContent = renderTooltip ? renderTooltip({
|
|
2436
|
+
...tooltipContext,
|
|
2437
|
+
colorScale
|
|
2438
|
+
}) : null;
|
|
2439
|
+
if (tooltipContent == null) return null;
|
|
2440
|
+
const scaleX = xScale;
|
|
2441
|
+
const scaleY = yScale;
|
|
2442
|
+
const getDatumLeftTop = (key, datum) => {
|
|
2443
|
+
const entry = dataRegistry?.get(key);
|
|
2444
|
+
return {
|
|
2445
|
+
left: scaleX && entry?.xAccessor ? Number(scaleX(entry.xAccessor(datum))) + scaleBandwidth(xScale) / 2 : void 0,
|
|
2446
|
+
top: scaleY && entry?.yAccessor ? Number(scaleY(entry.yAccessor(datum))) + scaleBandwidth(yScale) / 2 : void 0
|
|
2447
|
+
};
|
|
2448
|
+
};
|
|
2449
|
+
const nearestDatum = tooltipContext.tooltipData?.nearestDatum;
|
|
2450
|
+
let { tooltipLeft, tooltipTop } = tooltipContext;
|
|
2451
|
+
if (nearestDatum && (snapTooltipToDatumX || snapTooltipToDatumY || tooltipPlacement === "below-axis")) {
|
|
2452
|
+
const { left, top } = getDatumLeftTop(nearestDatum.key, nearestDatum.datum);
|
|
2453
|
+
if ((snapTooltipToDatumX || tooltipPlacement === "below-axis") && isValidNumber(left)) tooltipLeft = left;
|
|
2454
|
+
if (snapTooltipToDatumY && isValidNumber(top)) tooltipTop = top;
|
|
2455
|
+
}
|
|
2456
|
+
const glyphs = [];
|
|
2457
|
+
if (showDatumGlyph || showSeriesGlyphs) {
|
|
2458
|
+
const size = Number(glyphStyle?.radius ?? DEFAULT_GLYPH_RADIUS);
|
|
2459
|
+
const labelColor = theme?.htmlLabel?.color ?? FALLBACK_COLOR;
|
|
2460
|
+
const fallbackColor = showSeriesGlyphs ? labelColor : theme?.gridStyles?.stroke ?? labelColor;
|
|
2461
|
+
let entries = [];
|
|
2462
|
+
if (showSeriesGlyphs) entries = Object.values(tooltipContext.tooltipData?.datumByKey ?? {});
|
|
2463
|
+
else if (nearestDatum) entries = [nearestDatum];
|
|
2464
|
+
for (const { key, datum, index } of entries) {
|
|
2465
|
+
const { left, top } = getDatumLeftTop(key, datum);
|
|
2466
|
+
if (!isValidNumber(left) || !isValidNumber(top)) continue;
|
|
2467
|
+
glyphs.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", {
|
|
2468
|
+
className: "visx-tooltip-glyph",
|
|
2469
|
+
transform: `translate(${left}, ${top})`,
|
|
2470
|
+
children: renderGlyph({
|
|
2471
|
+
key,
|
|
2472
|
+
color: colorScale?.(key) ?? fallbackColor,
|
|
2473
|
+
datum,
|
|
2474
|
+
index,
|
|
2475
|
+
size,
|
|
2476
|
+
x: 0,
|
|
2477
|
+
y: 0,
|
|
2478
|
+
glyphStyle,
|
|
2479
|
+
isNearestDatum: nearestDatum?.key === key
|
|
2480
|
+
})
|
|
2481
|
+
}, key));
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
const crosshairStroke = theme?.gridStyles?.stroke ?? theme?.htmlLabel?.color ?? FALLBACK_COLOR;
|
|
2485
|
+
const marginTop = margin?.top ?? 0;
|
|
2486
|
+
const marginLeft = margin?.left ?? 0;
|
|
2487
|
+
const TooltipComponent = detectBounds || tooltipPlacement === "below-axis" ? BoundedTooltip : _visx_tooltip.Tooltip;
|
|
2488
|
+
const boxStyle = {
|
|
2489
|
+
..._visx_tooltip.defaultStyles,
|
|
2490
|
+
zIndex,
|
|
2491
|
+
backgroundColor: theme?.backgroundColor ?? "white",
|
|
2492
|
+
boxShadow: `0 1px 2px ${theme?.htmlLabel?.color ? `${theme.htmlLabel.color}55` : "#22222255"}`,
|
|
2493
|
+
...theme?.htmlLabel,
|
|
2494
|
+
...style
|
|
2495
|
+
};
|
|
2496
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", {
|
|
2497
|
+
className: "visx-tooltip-overlay",
|
|
2498
|
+
pointerEvents: "none",
|
|
2499
|
+
children: [
|
|
2500
|
+
showVerticalCrosshair && isValidNumber(tooltipLeft) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
|
|
2501
|
+
className: "visx-crosshair visx-crosshair-vertical",
|
|
2502
|
+
stroke: crosshairStroke,
|
|
2503
|
+
strokeWidth: CROSSHAIR_STROKE_WIDTH,
|
|
2504
|
+
...crosshairPaintProps(verticalCrosshairStyle),
|
|
2505
|
+
x1: tooltipLeft,
|
|
2506
|
+
x2: tooltipLeft,
|
|
2507
|
+
y1: marginTop,
|
|
2508
|
+
y2: marginTop + innerHeight
|
|
2509
|
+
}),
|
|
2510
|
+
showHorizontalCrosshair && isValidNumber(tooltipTop) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
|
|
2511
|
+
className: "visx-crosshair visx-crosshair-horizontal",
|
|
2512
|
+
stroke: crosshairStroke,
|
|
2513
|
+
strokeWidth: CROSSHAIR_STROKE_WIDTH,
|
|
2514
|
+
...crosshairPaintProps(horizontalCrosshairStyle),
|
|
2515
|
+
x1: marginLeft,
|
|
2516
|
+
x2: marginLeft + innerWidth,
|
|
2517
|
+
y1: tooltipTop,
|
|
2518
|
+
y2: tooltipTop
|
|
2519
|
+
}),
|
|
2520
|
+
glyphs
|
|
2521
|
+
]
|
|
2522
|
+
}), container && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipComponent, {
|
|
2523
|
+
left: tooltipLeft,
|
|
2524
|
+
top: tooltipPlacement === "below-axis" ? marginTop + innerHeight + (margin?.bottom ?? 0) : tooltipTop,
|
|
2525
|
+
style: boxStyle,
|
|
2526
|
+
applyPositionStyle: true,
|
|
2527
|
+
...tooltipProps,
|
|
2528
|
+
...tooltipPlacement === "below-axis" && { placement: tooltipPlacement },
|
|
2529
|
+
children: tooltipContent
|
|
2530
|
+
}), container)] });
|
|
2531
|
+
};
|
|
2532
|
+
/**
|
|
2533
|
+
* In-tree replacement for `@visx/xychart`'s `Tooltip`.
|
|
2534
|
+
*
|
|
2535
|
+
* visx renders its tooltip box, glyphs and crosshairs through portals appended
|
|
2536
|
+
* to `document.body`. That puts them in the page's root stacking context, where
|
|
2537
|
+
* they paint above every sticky or fixed element that lives inside a nested
|
|
2538
|
+
* stacking context, and no z-index on that element can change the order. This
|
|
2539
|
+
* component draws the glyphs and crosshairs straight into the chart SVG and
|
|
2540
|
+
* renders the tooltip box into the SVG's parent element, so all of them stack
|
|
2541
|
+
* as ordinary descendants of the chart.
|
|
2542
|
+
*
|
|
2543
|
+
* Render it as a child of `XYChart`. The element wrapping that `XYChart` must
|
|
2544
|
+
* be `position: relative` with the SVG at its origin, and `isolation: isolate`:
|
|
2545
|
+
* the box is placed with the SVG-local coordinates visx reports, and the
|
|
2546
|
+
* isolation keeps the box's `zIndex` from competing with page chrome. The box
|
|
2547
|
+
* flips and clamps to stay inside the nearest ancestor that clips its overflow
|
|
2548
|
+
* (or the viewport), so it may extend past the chart wrapper but is never cut
|
|
2549
|
+
* off unless that ancestor is smaller than the box itself. Below-axis placement
|
|
2550
|
+
* pins it beneath the x-axis and applies horizontal bounds only.
|
|
2551
|
+
*
|
|
2552
|
+
* @param props - visx's `Tooltip` options. `scroll`, `debounce` and `resizeObserverPolyfill` are accepted and ignored.
|
|
2553
|
+
* @return An anchor in the SVG, plus the overlay and the tooltip box while the tooltip is open.
|
|
2554
|
+
*/
|
|
2555
|
+
const XyChartTooltip = (props) => {
|
|
2556
|
+
const tooltipContext = (0, react.useContext)(_visx_xychart.TooltipContext);
|
|
2557
|
+
const [container, setContainer] = (0, react.useState)(null);
|
|
2558
|
+
const anchorRef = (0, react.useCallback)((node) => {
|
|
2559
|
+
setContainer(node?.ownerSVGElement?.parentElement ?? null);
|
|
2560
|
+
}, []);
|
|
2561
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", { ref: anchorRef }), tooltipContext?.tooltipOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(XyChartTooltipContent, {
|
|
2562
|
+
...props,
|
|
2563
|
+
tooltipContext,
|
|
2564
|
+
container
|
|
2565
|
+
})] });
|
|
2566
|
+
};
|
|
2567
|
+
//#endregion
|
|
2131
2568
|
//#region src/components/tooltip/accessible-tooltip.tsx
|
|
2132
2569
|
const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardFocusedClassName, series = [], mode = "group", verticalCrosshairStyle, horizontalCrosshairStyle, ...props }) => {
|
|
2133
2570
|
const tooltipContext = (0, react.useContext)(_visx_xychart.TooltipContext);
|
|
2134
2571
|
const scopeElement = useChartScopeElement();
|
|
2135
|
-
const gridStroke = useGlobalChartsTheme().gridStyles?.stroke;
|
|
2136
2572
|
const crosshairStroke = (0, react.useMemo)(() => {
|
|
2137
|
-
const stroke =
|
|
2573
|
+
const stroke = resolveCssVariable(CATALOG_POINTERS.grid, scopeElement);
|
|
2138
2574
|
return stroke ? { stroke } : void 0;
|
|
2139
|
-
}, [
|
|
2575
|
+
}, [scopeElement]);
|
|
2576
|
+
const standaloneScopeClass = useStandaloneScopeClass();
|
|
2140
2577
|
const tooltipData = (0, react.useMemo)(() => {
|
|
2141
2578
|
if (mode !== "individual") return [];
|
|
2142
2579
|
if (series.length === 0) return [];
|
|
@@ -2197,11 +2634,11 @@ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardF
|
|
|
2197
2634
|
tabIndex: -1,
|
|
2198
2635
|
role: "tooltip",
|
|
2199
2636
|
"aria-atomic": "true",
|
|
2200
|
-
className: (0, clsx.default)(
|
|
2637
|
+
className: (0, clsx.default)(standaloneScopeClass, keyboardFocusedClassName),
|
|
2201
2638
|
children: tooltipContent
|
|
2202
2639
|
}, `chart-tooltip-${selectedIndex}`);
|
|
2203
2640
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2204
|
-
className:
|
|
2641
|
+
className: standaloneScopeClass,
|
|
2205
2642
|
role: "tooltip",
|
|
2206
2643
|
"aria-live": "polite",
|
|
2207
2644
|
children: tooltipContent
|
|
@@ -2211,9 +2648,10 @@ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardF
|
|
|
2211
2648
|
renderTooltip,
|
|
2212
2649
|
selectedIndex,
|
|
2213
2650
|
tooltipRef,
|
|
2214
|
-
keyboardFocusedClassName
|
|
2651
|
+
keyboardFocusedClassName,
|
|
2652
|
+
standaloneScopeClass
|
|
2215
2653
|
]);
|
|
2216
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
2654
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(XyChartTooltip, {
|
|
2217
2655
|
...props,
|
|
2218
2656
|
verticalCrosshairStyle: {
|
|
2219
2657
|
...crosshairStroke,
|
|
@@ -2226,11 +2664,14 @@ const AccessibleTooltip = ({ renderTooltip, selectedIndex, tooltipRef, keyboardF
|
|
|
2226
2664
|
renderTooltip: focusableRenderTooltip
|
|
2227
2665
|
});
|
|
2228
2666
|
};
|
|
2229
|
-
const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating, setIsNavigating, chartRef, totalPoints, onActivate }) => {
|
|
2667
|
+
const useKeyboardNavigation = ({ selectedIndex, setSelectedIndex, isNavigating, setIsNavigating, chartRef, totalPoints, onActivate, preventTooltipScroll = false }) => {
|
|
2230
2668
|
return {
|
|
2231
2669
|
tooltipRef: (0, react.useCallback)((element) => {
|
|
2232
|
-
if (element && selectedIndex !== void 0)
|
|
2233
|
-
|
|
2670
|
+
if (element && selectedIndex !== void 0) {
|
|
2671
|
+
if (preventTooltipScroll) element.focus({ preventScroll: true });
|
|
2672
|
+
else element.focus();
|
|
2673
|
+
}
|
|
2674
|
+
}, [preventTooltipScroll, selectedIndex]),
|
|
2234
2675
|
onChartFocus: (0, react.useCallback)(() => {
|
|
2235
2676
|
if (!isNavigating && selectedIndex !== void 0) setSelectedIndex(0);
|
|
2236
2677
|
}, [
|
|
@@ -2561,72 +3002,90 @@ const getCurveType = (type, smoothing) => {
|
|
|
2561
3002
|
default: return _visx_curve.curveLinear;
|
|
2562
3003
|
}
|
|
2563
3004
|
};
|
|
2564
|
-
const
|
|
2565
|
-
|
|
2566
|
-
return (timestamp) => {
|
|
2567
|
-
if (!Number.isFinite(Number(timestamp))) return new Date(timestamp).toLocaleDateString();
|
|
2568
|
-
formatter = formatter ?? new Intl.DateTimeFormat(void 0, options);
|
|
2569
|
-
return formatter.format(timestamp);
|
|
2570
|
-
};
|
|
2571
|
-
};
|
|
2572
|
-
const formatYearTick = dateTimeFormat({ year: "numeric" });
|
|
2573
|
-
const formatDateTick = dateTimeFormat({
|
|
3005
|
+
const YEAR_TICK = { year: "numeric" };
|
|
3006
|
+
const DATE_TICK = {
|
|
2574
3007
|
month: "short",
|
|
2575
3008
|
day: "numeric"
|
|
2576
|
-
}
|
|
2577
|
-
const
|
|
2578
|
-
|
|
2579
|
-
hour12: true
|
|
2580
|
-
});
|
|
2581
|
-
const formatMonthTick = dateTimeFormat({ month: "short" });
|
|
3009
|
+
};
|
|
3010
|
+
const HOUR_TICK = { hour: "numeric" };
|
|
3011
|
+
const MONTH_TICK = { month: "short" };
|
|
2582
3012
|
const boundaryFormat = (isAnchor, atBoundary, between) => {
|
|
2583
3013
|
const format = (timestamp) => isAnchor(new Date(timestamp)) ? atBoundary(timestamp) : between(timestamp);
|
|
2584
3014
|
format.isAnchor = isAnchor;
|
|
2585
3015
|
return format;
|
|
2586
3016
|
};
|
|
2587
|
-
const
|
|
2588
|
-
const
|
|
3017
|
+
const tickFormats = (formatting) => {
|
|
3018
|
+
const clock = createZonedClock(formatting.timeZone);
|
|
3019
|
+
const year = createDateFormatter(YEAR_TICK, formatting);
|
|
3020
|
+
const date = createDateFormatter(DATE_TICK, formatting);
|
|
3021
|
+
const hour = createDateFormatter(HOUR_TICK, formatting);
|
|
3022
|
+
const month = createDateFormatter(MONTH_TICK, formatting);
|
|
3023
|
+
const datedTick = (timestamp) => date(timestamp);
|
|
3024
|
+
datedTick.anchorsAtLabelChange = true;
|
|
3025
|
+
return {
|
|
3026
|
+
year,
|
|
3027
|
+
date: datedTick,
|
|
3028
|
+
dateOrHour: boundaryFormat((value) => clock(value).hour === 0, date, hour),
|
|
3029
|
+
hour,
|
|
3030
|
+
monthOrYear: boundaryFormat((value) => clock(value).month === 1, year, month)
|
|
3031
|
+
};
|
|
3032
|
+
};
|
|
2589
3033
|
const getSpan = (sortedData) => {
|
|
2590
|
-
const bounds = sortedData.map((datom) => [datom.data.at(0)?.date, datom.data.at(-1)?.date]).filter(([first, last]) => first
|
|
3034
|
+
const bounds = sortedData.map((datom) => [datom.data.at(0)?.date, datom.data.at(-1)?.date]).filter(([first, last]) => Number.isFinite(Number(first)) && Number.isFinite(Number(last)));
|
|
2591
3035
|
if (!bounds.length) return null;
|
|
2592
3036
|
return {
|
|
2593
3037
|
minX: Math.min(...bounds.map(([first]) => Number(first))),
|
|
2594
3038
|
maxX: Math.max(...bounds.map(([, last]) => Number(last)))
|
|
2595
3039
|
};
|
|
2596
3040
|
};
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
const
|
|
2607
|
-
|
|
2608
|
-
const spacingInHours = getPointSpacingInHours(sortedData);
|
|
2609
|
-
if (spacingInHours < SUB_DAILY_SPACING_HOURS) return "hour";
|
|
2610
|
-
if (!Number.isFinite(spacingInHours) || spacingInHours < MONTHLY_SPACING_HOURS) return "day";
|
|
2611
|
-
return spacingInHours < 12 * MONTHLY_SPACING_HOURS ? "month" : "year";
|
|
2612
|
-
};
|
|
2613
|
-
const getFormatter = (sortedData, tickResolution) => {
|
|
3041
|
+
/**
|
|
3042
|
+
* The most informative tick format for a bucket resolution and the span on screen.
|
|
3043
|
+
*
|
|
3044
|
+
* @param sortedData - Series as returned by `useChartDataTransform`.
|
|
3045
|
+
* @param tickResolution - Caller-declared bucket resolution, when known.
|
|
3046
|
+
* @param formatting - Host locale and time zone.
|
|
3047
|
+
* @param domain - The effective scale domain, or undefined for the data's own extent.
|
|
3048
|
+
* @return The formatter the axis labels its ticks with.
|
|
3049
|
+
*/
|
|
3050
|
+
const getFormatter = (sortedData, tickResolution, formatting = {}, domain) => {
|
|
3051
|
+
const format = tickFormats(formatting);
|
|
2614
3052
|
const resolution = getBucketResolution(sortedData, tickResolution);
|
|
2615
|
-
if (resolution === "year") return
|
|
2616
|
-
if (resolution === "month") return
|
|
2617
|
-
const span =
|
|
2618
|
-
|
|
3053
|
+
if (resolution === "year") return format.year;
|
|
3054
|
+
if (resolution === "month") return format.monthOrYear;
|
|
3055
|
+
const span = domain ? {
|
|
3056
|
+
minX: domain[0].getTime(),
|
|
3057
|
+
maxX: domain[1].getTime()
|
|
3058
|
+
} : getSpan(sortedData);
|
|
3059
|
+
if (!span) return format.date;
|
|
2619
3060
|
const diffInHours = Math.abs((0, date_fns.differenceInHours)(span.maxX, span.minX));
|
|
2620
3061
|
if (resolution === "hour") {
|
|
2621
|
-
if (diffInHours
|
|
2622
|
-
if (diffInHours <= 168) return
|
|
3062
|
+
if (diffInHours < 24) return format.hour;
|
|
3063
|
+
if (diffInHours <= 168) return format.dateOrHour;
|
|
2623
3064
|
}
|
|
2624
|
-
return Math.abs((0, date_fns.differenceInYears)(span.maxX, span.minX)) <= 1 ?
|
|
3065
|
+
return Math.abs((0, date_fns.differenceInYears)(span.maxX, span.minX)) <= 1 ? format.date : format.year;
|
|
3066
|
+
};
|
|
3067
|
+
/**
|
|
3068
|
+
* The instants a set of series spans, as a scale domain.
|
|
3069
|
+
*
|
|
3070
|
+
* @param sortedData - Series as returned by `useChartDataTransform`.
|
|
3071
|
+
* @return Earliest and latest dated point, or undefined when nothing is dated.
|
|
3072
|
+
*/
|
|
3073
|
+
const getSeriesExtent = (sortedData) => {
|
|
3074
|
+
const span = getSpan(sortedData);
|
|
3075
|
+
return span ? [new Date(span.minX), new Date(span.maxX)] : void 0;
|
|
3076
|
+
};
|
|
3077
|
+
const getAnchorIndices = (domain, tickFormatter, labels) => {
|
|
3078
|
+
if (tickFormatter.anchorsAtLabelChange) return labels.reduce((indices, label, index) => {
|
|
3079
|
+
if (index === 0 || label !== labels[index - 1]) indices.push(index);
|
|
3080
|
+
return indices;
|
|
3081
|
+
}, []);
|
|
3082
|
+
const { isAnchor } = tickFormatter;
|
|
3083
|
+
if (!isAnchor) return [];
|
|
3084
|
+
return domain.reduce((indices, date, index) => {
|
|
3085
|
+
if (isAnchor(date)) indices.push(index);
|
|
3086
|
+
return indices;
|
|
3087
|
+
}, []);
|
|
2625
3088
|
};
|
|
2626
|
-
const getAnchorIndices = (domain, isAnchor) => domain.reduce((indices, date, index) => {
|
|
2627
|
-
if (isAnchor(date)) indices.push(index);
|
|
2628
|
-
return indices;
|
|
2629
|
-
}, []);
|
|
2630
3089
|
const getCandidateSteps = (length, maxTicks, anchorSpacing) => {
|
|
2631
3090
|
const steps = /* @__PURE__ */ new Set();
|
|
2632
3091
|
for (let count = maxTicks; count > 1; count--) {
|
|
@@ -2641,26 +3100,25 @@ const getCandidateSteps = (length, maxTicks, anchorSpacing) => {
|
|
|
2641
3100
|
return steps;
|
|
2642
3101
|
};
|
|
2643
3102
|
/**
|
|
2644
|
-
* Tick values for
|
|
3103
|
+
* Tick values sampled by index, for the bar chart's band axis and for evenly
|
|
3104
|
+
* spaced points on a continuous one.
|
|
2645
3105
|
*
|
|
2646
|
-
* visx samples
|
|
2647
|
-
*
|
|
2648
|
-
*
|
|
2649
|
-
*
|
|
2650
|
-
*
|
|
2651
|
-
*
|
|
2652
|
-
* identical labels side by side.
|
|
3106
|
+
* visx samples by index from offset zero, blind to which labels carry a boundary
|
|
3107
|
+
* and without collapsing repeats — so the tick that prints the year or the date
|
|
3108
|
+
* often isn't sampled at all, and a long series can show the same label twice.
|
|
3109
|
+
* Choose the values instead: sweep the evenly spaced candidates, including those
|
|
3110
|
+
* that step from anchor to anchor, and keep the one that reaches the most anchors
|
|
3111
|
+
* without thinning the axis or putting two identical labels side by side.
|
|
2653
3112
|
*
|
|
2654
|
-
* @param domain -
|
|
3113
|
+
* @param domain - Buckets in axis order.
|
|
2655
3114
|
* @param tickFormatter - Formatter the axis will render these values with.
|
|
2656
3115
|
* @param maxTicks - Most ticks the axis should carry.
|
|
2657
3116
|
* @return Values to hand the axis as `tickValues`.
|
|
2658
3117
|
*/
|
|
2659
3118
|
const getBandTickValues = (domain, tickFormatter, maxTicks) => {
|
|
2660
3119
|
if (!domain.length) return [];
|
|
2661
|
-
const { isAnchor } = tickFormatter;
|
|
2662
|
-
const anchorIndices = isAnchor ? getAnchorIndices(domain, isAnchor) : [];
|
|
2663
3120
|
const domainLabels = domain.map((date) => tickFormatter(date.getTime()));
|
|
3121
|
+
const anchorIndices = getAnchorIndices(domain, tickFormatter, domainLabels);
|
|
2664
3122
|
const candidates = [];
|
|
2665
3123
|
const consider = (indices) => {
|
|
2666
3124
|
if (!indices.length || indices.length > maxTicks) return;
|
|
@@ -2672,15 +3130,108 @@ const getBandTickValues = (domain, tickFormatter, maxTicks) => {
|
|
|
2672
3130
|
for (let index = offset; index < domain.length; index += step) indices.push(index);
|
|
2673
3131
|
consider(indices);
|
|
2674
3132
|
}
|
|
2675
|
-
|
|
3133
|
+
const minStride = Math.max(1, Math.ceil(anchorIndices.length / maxTicks));
|
|
3134
|
+
for (let stride = minStride; stride <= anchorIndices.length; stride++) {
|
|
3135
|
+
const stepped = [];
|
|
3136
|
+
for (let position = 0; position < anchorIndices.length; position += stride) stepped.push(anchorIndices[position]);
|
|
3137
|
+
consider(stepped);
|
|
3138
|
+
}
|
|
2676
3139
|
if (!candidates.length) return [domain[0]];
|
|
2677
3140
|
const densest = candidates.reduce((most, indices) => Math.max(most, indices.length), 0);
|
|
2678
|
-
const
|
|
3141
|
+
const anchorIndexSet = new Set(anchorIndices);
|
|
3142
|
+
const anchorsIn = (indices) => indices.filter((index) => anchorIndexSet.has(index)).length;
|
|
2679
3143
|
return candidates.filter((indices) => indices.length >= densest - 1).reduce((chosen, indices) => {
|
|
2680
3144
|
const gain = anchorsIn(indices) - anchorsIn(chosen);
|
|
2681
3145
|
return gain > 0 || gain === 0 && indices.length > chosen.length ? indices : chosen;
|
|
2682
3146
|
}).map((index) => domain[index]);
|
|
2683
3147
|
};
|
|
3148
|
+
/**
|
|
3149
|
+
* The most x-axis ticks a chart of this width has room for.
|
|
3150
|
+
*
|
|
3151
|
+
* @param chartWidth - Chart width in pixels.
|
|
3152
|
+
* @return A tick budget of at least one.
|
|
3153
|
+
*/
|
|
3154
|
+
const getMaxTicksForWidth = (chartWidth) => Math.max(1, Math.floor(chartWidth / X_TICK_WIDTH));
|
|
3155
|
+
const MIN_GAP_IN_LABELS = .9;
|
|
3156
|
+
const nearestValue = (sorted, target) => {
|
|
3157
|
+
let low = 0;
|
|
3158
|
+
let high = sorted.length - 1;
|
|
3159
|
+
while (low < high) {
|
|
3160
|
+
const middle = Math.floor((low + high) / 2);
|
|
3161
|
+
if (sorted[middle] < target) low = middle + 1;
|
|
3162
|
+
else high = middle;
|
|
3163
|
+
}
|
|
3164
|
+
const above = sorted[low];
|
|
3165
|
+
const below = low > 0 ? sorted[low - 1] : above;
|
|
3166
|
+
return Math.abs(above - target) <= Math.abs(below - target) ? above : below;
|
|
3167
|
+
};
|
|
3168
|
+
const getPositionTickValues = (points, tickFormatter, maxTicks, axis) => {
|
|
3169
|
+
const times = points.map((point) => point.getTime());
|
|
3170
|
+
const { first, last } = axis;
|
|
3171
|
+
if (maxTicks <= 1 || times.length === 1 || first === last) return [points[0]];
|
|
3172
|
+
const step = (last - first) / (maxTicks - 1);
|
|
3173
|
+
const labels = points.map((point) => tickFormatter(point.getTime()));
|
|
3174
|
+
const minGap = (last - first) / maxTicks * MIN_GAP_IN_LABELS;
|
|
3175
|
+
const anchors = getAnchorIndices(points, tickFormatter, labels).map((index) => times[index]);
|
|
3176
|
+
const chosen = [];
|
|
3177
|
+
let lastLabel = null;
|
|
3178
|
+
for (let index = 0; index < maxTicks; index++) {
|
|
3179
|
+
const target = first + index * step;
|
|
3180
|
+
const anchor = anchors.length ? nearestValue(anchors, target) : null;
|
|
3181
|
+
let tick = anchor !== null && Math.abs(anchor - target) <= step / 2 ? anchor : null;
|
|
3182
|
+
if (tick === null) {
|
|
3183
|
+
const point = nearestValue(times, target);
|
|
3184
|
+
tick = Math.abs(point - target) <= step / 2 ? point : target;
|
|
3185
|
+
}
|
|
3186
|
+
const previous = chosen[chosen.length - 1];
|
|
3187
|
+
if (previous !== void 0 && (tick <= previous || tick - previous < minGap)) continue;
|
|
3188
|
+
const label = tickFormatter(tick);
|
|
3189
|
+
if (label === lastLabel) continue;
|
|
3190
|
+
chosen.push(tick);
|
|
3191
|
+
lastLabel = label;
|
|
3192
|
+
}
|
|
3193
|
+
return chosen.map((timestamp) => new Date(timestamp));
|
|
3194
|
+
};
|
|
3195
|
+
const isIndexSamplingUnusable = (ticks, points, maxTicks, axis) => {
|
|
3196
|
+
if (ticks.length < 2 || points.length < 2) return false;
|
|
3197
|
+
const span = axis.last - axis.first;
|
|
3198
|
+
if (!span) return false;
|
|
3199
|
+
return ticks.slice(1).reduce((nearest, tick, index) => Math.min(nearest, tick.getTime() - ticks[index].getTime()), Infinity) < span / maxTicks * MIN_GAP_IN_LABELS || ticks.length < Math.min(maxTicks, points.length) / 3;
|
|
3200
|
+
};
|
|
3201
|
+
/**
|
|
3202
|
+
* Tick values for a continuous time axis, chosen from the points it draws.
|
|
3203
|
+
*
|
|
3204
|
+
* Sampled by index while the points are evenly spaced, which keeps the axis on
|
|
3205
|
+
* the calendar boundaries `getBandTickValues` steers towards, and by position
|
|
3206
|
+
* once they are not, where an index stride crowds one end of the scale.
|
|
3207
|
+
*
|
|
3208
|
+
* Values are taken from the data wherever there is one to take, so a local time
|
|
3209
|
+
* that does not exist cannot become a tick and DST needs no special case. Only a
|
|
3210
|
+
* target stranded in a gap is constructed, as a plain instant.
|
|
3211
|
+
*
|
|
3212
|
+
* @param sortedData - Series as returned by `useChartDataTransform`.
|
|
3213
|
+
* @param domain - The effective scale domain, or undefined for all data.
|
|
3214
|
+
* @param tickFormatter - The formatter the ticks will be labeled with.
|
|
3215
|
+
* @param maxTicks - The most ticks the axis has room for.
|
|
3216
|
+
* @return Tick dates, or null when no series carries a usable date.
|
|
3217
|
+
*/
|
|
3218
|
+
const getTimeAxisTickValues = (sortedData, domain, tickFormatter, maxTicks) => {
|
|
3219
|
+
const byTimestamp = /* @__PURE__ */ new Map();
|
|
3220
|
+
for (const series of sortedData) for (const point of series.data) {
|
|
3221
|
+
const date = point.date;
|
|
3222
|
+
if (date instanceof Date && Number.isFinite(date.getTime())) byTimestamp.set(date.getTime(), date);
|
|
3223
|
+
}
|
|
3224
|
+
if (!byTimestamp.size) return null;
|
|
3225
|
+
const min = domain?.[0]?.getTime();
|
|
3226
|
+
const max = domain?.[1]?.getTime();
|
|
3227
|
+
const visible = [...byTimestamp.keys()].sort((a, b) => a - b).filter((timestamp) => (min === void 0 || timestamp >= min) && (max === void 0 || timestamp <= max)).map((timestamp) => byTimestamp.get(timestamp));
|
|
3228
|
+
const axis = {
|
|
3229
|
+
first: min ?? visible[0].getTime(),
|
|
3230
|
+
last: max ?? visible[visible.length - 1].getTime()
|
|
3231
|
+
};
|
|
3232
|
+
const ticks = getBandTickValues(visible, tickFormatter, maxTicks);
|
|
3233
|
+
return isIndexSamplingUnusable(ticks, visible, maxTicks, axis) ? getPositionTickValues(visible, tickFormatter, maxTicks, axis) : ticks;
|
|
3234
|
+
};
|
|
2684
3235
|
const guessOptimalNumTicks = (data, chartWidth, tickFormatter) => {
|
|
2685
3236
|
const span = getSpan(data);
|
|
2686
3237
|
if (!span) return 1;
|
|
@@ -2695,7 +3246,43 @@ const guessOptimalNumTicks = (data, chartWidth, tickFormatter) => {
|
|
|
2695
3246
|
if (ticks.some((tick, idx) => idx > 0 && tick === ticks[idx - 1])) continue;
|
|
2696
3247
|
return ticks.length;
|
|
2697
3248
|
}
|
|
2698
|
-
return secondBestGuess;
|
|
3249
|
+
return Math.max(1, secondBestGuess);
|
|
3250
|
+
};
|
|
3251
|
+
//#endregion
|
|
3252
|
+
//#region src/charts/private/time-axis-options.ts
|
|
3253
|
+
const toDateDomain = (domain) => domain ? [new Date(domain[0]), new Date(domain[1])] : void 0;
|
|
3254
|
+
/**
|
|
3255
|
+
* The x-axis options for a continuous time chart, shared by LineChart and AreaChart.
|
|
3256
|
+
*
|
|
3257
|
+
* Kept in one place so the tick-selection logic isn't duplicated between them.
|
|
3258
|
+
*
|
|
3259
|
+
* @param args - Named arguments.
|
|
3260
|
+
* @param args.dataSorted - Series as returned by `useChartDataTransform`.
|
|
3261
|
+
* @param args.width - Chart width in pixels, which bounds how many ticks fit.
|
|
3262
|
+
* @param args.axisOptions - The caller's `options.axis.x`.
|
|
3263
|
+
* @param args.scaleDomain - The caller's `options.xScale.domain`.
|
|
3264
|
+
* @param args.zoomDomain - The chart's current zoom window, when zoomed.
|
|
3265
|
+
* @param args.formatting - Host locale and time zone.
|
|
3266
|
+
* @param args.isSeriesRendered - Whether visx mounts a series, i.e. the legend shows it. Defaults to all of them.
|
|
3267
|
+
* @return Options ready to spread into the chart's `axis.x`; `orientation` is always set.
|
|
3268
|
+
*/
|
|
3269
|
+
const buildTimeAxisOptions = ({ dataSorted, width, axisOptions, scaleDomain, zoomDomain, formatting, isSeriesRendered }) => {
|
|
3270
|
+
const { tickResolution, tickFormat, tickValues: callerTickValues, numTicks: callerNumTicks, orientation: callerOrientation, display: callerDisplay, ...rest } = axisOptions ?? {};
|
|
3271
|
+
const rendered = isSeriesRendered ? dataSorted.filter(isSeriesRendered) : dataSorted;
|
|
3272
|
+
const effectiveDomain = zoomDomain ?? toDateDomain(scaleDomain) ?? getSeriesExtent(rendered);
|
|
3273
|
+
const ownFormatter = getFormatter(rendered, tickResolution, formatting, effectiveDomain);
|
|
3274
|
+
const formatter = tickFormat || ownFormatter;
|
|
3275
|
+
const ownTickValues = tickFormat || callerTickValues ? null : getTimeAxisTickValues(rendered, effectiveDomain, ownFormatter, callerNumTicks ?? getMaxTicksForWidth(width));
|
|
3276
|
+
const tickValues = callerTickValues ?? (ownTickValues?.length ? ownTickValues : void 0);
|
|
3277
|
+
const numTicks = callerNumTicks ?? (tickValues ? void 0 : guessOptimalNumTicks(rendered, width, formatter));
|
|
3278
|
+
return {
|
|
3279
|
+
orientation: callerOrientation ?? "bottom",
|
|
3280
|
+
tickFormat: formatter,
|
|
3281
|
+
display: callerDisplay ?? true,
|
|
3282
|
+
...rest,
|
|
3283
|
+
...tickValues ? { tickValues } : {},
|
|
3284
|
+
...numTicks === void 0 ? {} : { numTicks }
|
|
3285
|
+
};
|
|
2699
3286
|
};
|
|
2700
3287
|
//#endregion
|
|
2701
3288
|
//#region src/charts/private/with-responsive/with-responsive.module.scss
|
|
@@ -2976,6 +3563,9 @@ function ZoomResetButton({ onClick }) {
|
|
|
2976
3563
|
});
|
|
2977
3564
|
}
|
|
2978
3565
|
//#endregion
|
|
3566
|
+
//#region src/charts/private/xy-plot/xy-plot.module.scss
|
|
3567
|
+
var xy_plot_module_default = { "xy-plot": "a8ccharts-bIyhgG-xy-plot" };
|
|
3568
|
+
//#endregion
|
|
2979
3569
|
//#region src/charts/line-chart/line-chart.module.scss
|
|
2980
3570
|
var line_chart_module_default = {
|
|
2981
3571
|
"line-chart": "a8ccharts-inuQka-line-chart",
|
|
@@ -3216,12 +3806,14 @@ const getVerticalAnchor = (subjectType, isFlippedVertically, y, yMax, height) =>
|
|
|
3216
3806
|
};
|
|
3217
3807
|
const LineChartAnnotation = ({ datum, title, subtitle, subjectType = "circle", styles: datumStyles, testId, renderLabel, renderLabelPopover }) => {
|
|
3218
3808
|
const providerTheme = useGlobalChartsTheme();
|
|
3219
|
-
const scopeElement = useChartScopeElement();
|
|
3220
3809
|
const { xScale, yScale } = (0, react.useContext)(_visx_xychart.DataContext) || {};
|
|
3221
3810
|
const labelRef = (0, react.useRef)(null);
|
|
3222
3811
|
const [height, setHeight] = (0, react.useState)(null);
|
|
3223
|
-
const styles = (0,
|
|
3224
|
-
|
|
3812
|
+
const styles = (0, react.useMemo)(() => deepmerge.default.all([
|
|
3813
|
+
ANNOTATION_POINTERS,
|
|
3814
|
+
providerTheme.annotationStyles ?? {},
|
|
3815
|
+
datumStyles ?? {}
|
|
3816
|
+
]), [providerTheme.annotationStyles, datumStyles]);
|
|
3225
3817
|
(0, react.useEffect)(() => {
|
|
3226
3818
|
if (labelRef.current?.getBBox) {
|
|
3227
3819
|
const bbox = labelRef.current.getBBox();
|
|
@@ -3307,15 +3899,8 @@ const LineChartAnnotation = ({ datum, title, subtitle, subjectType = "circle", s
|
|
|
3307
3899
|
dx,
|
|
3308
3900
|
dy,
|
|
3309
3901
|
children: [
|
|
3310
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_annotation.Connector, {
|
|
3311
|
-
|
|
3312
|
-
stroke: resolveColor(styles?.connector?.stroke)
|
|
3313
|
-
}),
|
|
3314
|
-
subjectType === "circle" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_annotation.CircleSubject, {
|
|
3315
|
-
...styles?.circleSubject,
|
|
3316
|
-
fill: resolveColor(styles?.circleSubject?.fill),
|
|
3317
|
-
stroke: resolveColor(styles?.circleSubject?.stroke)
|
|
3318
|
-
}),
|
|
3902
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_annotation.Connector, { ...styles?.connector }),
|
|
3903
|
+
subjectType === "circle" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_annotation.CircleSubject, { ...styles?.circleSubject }),
|
|
3319
3904
|
subjectType === "line-vertical" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_annotation.LineSubject, {
|
|
3320
3905
|
min: yMax,
|
|
3321
3906
|
max: yMin,
|
|
@@ -3349,8 +3934,6 @@ const LineChartAnnotation = ({ datum, title, subtitle, subjectType = "circle", s
|
|
|
3349
3934
|
title,
|
|
3350
3935
|
subtitle,
|
|
3351
3936
|
...styles?.label,
|
|
3352
|
-
anchorLineStroke: resolveColor(styles?.label?.anchorLineStroke),
|
|
3353
|
-
backgroundFill: resolveColor(styles?.label?.backgroundFill),
|
|
3354
3937
|
...labelPosition,
|
|
3355
3938
|
horizontalAnchor: getHorizontalAnchor(subjectType, isFlippedHorizontally),
|
|
3356
3939
|
verticalAnchor: getVerticalAnchor(subjectType, isFlippedVertically, y, yMax, height ?? ANNOTATION_INIT_HEIGHT)
|
|
@@ -3398,16 +3981,34 @@ const toNumber = (val) => {
|
|
|
3398
3981
|
const num = typeof val === "number" ? val : parseFloat(val);
|
|
3399
3982
|
return isNaN(num) ? void 0 : num;
|
|
3400
3983
|
};
|
|
3984
|
+
const TOOLTIP_DATE = {};
|
|
3985
|
+
const TOOLTIP_FORMAT_BY_RESOLUTION$1 = {
|
|
3986
|
+
hour: {
|
|
3987
|
+
year: "numeric",
|
|
3988
|
+
month: "numeric",
|
|
3989
|
+
day: "numeric",
|
|
3990
|
+
hour: "numeric"
|
|
3991
|
+
},
|
|
3992
|
+
day: TOOLTIP_DATE,
|
|
3993
|
+
month: TOOLTIP_DATE,
|
|
3994
|
+
year: TOOLTIP_DATE
|
|
3995
|
+
};
|
|
3996
|
+
const TooltipDate = ({ date, displayResolution }) => {
|
|
3997
|
+
const formatting = useChartFormatting();
|
|
3998
|
+
const format = (0, react.useMemo)(() => createDateFormatter(TOOLTIP_FORMAT_BY_RESOLUTION$1[displayResolution], formatting), [displayResolution, formatting]);
|
|
3999
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: date ? format(date) : null });
|
|
4000
|
+
};
|
|
3401
4001
|
/**
|
|
3402
4002
|
* Default visx-tooltip render that prints the hovered date as a heading and
|
|
3403
4003
|
* one row per visible series (label + formatted value), sorted descending by
|
|
3404
4004
|
* value. Reused by AreaChart, which has the same multi-series shape.
|
|
3405
4005
|
*
|
|
3406
|
-
* @param params
|
|
4006
|
+
* @param params - visx tooltip data and the chart's optional `bucketInfo`.
|
|
4007
|
+
* @param contentStyle - Explicit tooltip content color overrides.
|
|
3407
4008
|
* @return Tooltip JSX, or `null` when no datum is hovered.
|
|
3408
4009
|
*/
|
|
3409
|
-
const renderDefaultTooltip = (params) => {
|
|
3410
|
-
const { tooltipData } = params;
|
|
4010
|
+
const renderDefaultTooltip = (params, contentStyle) => {
|
|
4011
|
+
const { tooltipData, bucketInfo } = params;
|
|
3411
4012
|
const nearestDatum = tooltipData?.nearestDatum?.datum;
|
|
3412
4013
|
if (!nearestDatum) return null;
|
|
3413
4014
|
const tooltipPoints = Object.entries(tooltipData?.datumByKey || {}).map(([key, { datum }]) => ({
|
|
@@ -3416,9 +4017,13 @@ const renderDefaultTooltip = (params) => {
|
|
|
3416
4017
|
})).sort((a, b) => b.value - a.value);
|
|
3417
4018
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3418
4019
|
className: line_chart_module_default["line-chart__tooltip"],
|
|
4020
|
+
style: contentStyle,
|
|
3419
4021
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3420
4022
|
className: line_chart_module_default["line-chart__tooltip-date"],
|
|
3421
|
-
children:
|
|
4023
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipDate, {
|
|
4024
|
+
date: nearestDatum.date,
|
|
4025
|
+
displayResolution: bucketInfo?.displayResolution ?? "day"
|
|
4026
|
+
})
|
|
3422
4027
|
}), tooltipPoints.map((point) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_wordpress_ui.Stack, {
|
|
3423
4028
|
direction: "row",
|
|
3424
4029
|
align: "center",
|
|
@@ -3462,14 +4067,14 @@ const LineChartScalesRef = ({ chartRef, width, height, margin }) => {
|
|
|
3462
4067
|
]);
|
|
3463
4068
|
return null;
|
|
3464
4069
|
};
|
|
3465
|
-
const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, renderGlyph = defaultRenderGlyph, glyphStyle = {}, withLegendGlyph = false, withGradientFill = false, smoothing = true, curveType, renderTooltip = renderDefaultTooltip, withStartGlyphs = false, withEndGlyphs = false, animation, options = {}, onPointerDown = void 0, onPointerUp = void 0, onPointerMove = void 0, onPointerOut = void 0, onDatumActivate = void 0, zoomable = false, rescaleYOnVisibilityChange = true, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
|
|
4070
|
+
const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, renderGlyph = defaultRenderGlyph, glyphStyle = {}, withLegendGlyph = false, withGradientFill = false, smoothing = true, curveType, renderTooltip = renderDefaultTooltip, tooltipPlacement, tooltipStyle, withStartGlyphs = false, withEndGlyphs = false, animation, options = {}, onPointerDown = void 0, onPointerUp = void 0, onPointerMove = void 0, onPointerOut = void 0, onDatumActivate = void 0, zoomable = false, rescaleYOnVisibilityChange = true, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
|
|
3466
4071
|
const legendInteractive = legend.interactive ?? false;
|
|
3467
4072
|
const legendCollapseGroups = legend.collapseGroups ?? false;
|
|
3468
4073
|
const legendShape = legend.shape ?? "line";
|
|
3469
4074
|
const legendPosition = legend.position ?? "bottom";
|
|
3470
|
-
const
|
|
4075
|
+
const formatting = useChartFormatting();
|
|
3471
4076
|
const theme = useXYChartTheme(data);
|
|
3472
|
-
const resolvedBackgroundColor = theme.backgroundColor
|
|
4077
|
+
const resolvedBackgroundColor = theme.backgroundColor;
|
|
3473
4078
|
const chartId = useChartId(providedChartId);
|
|
3474
4079
|
const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
|
|
3475
4080
|
const isSeriesVisible = (0, react.useCallback)((seriesLabel) => !hiddenSeries.has(seriesLabel), [hiddenSeries]);
|
|
@@ -3540,20 +4145,21 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
3540
4145
|
setIsNavigating,
|
|
3541
4146
|
chartRef,
|
|
3542
4147
|
totalPoints: dataSorted[0]?.data.length || 0,
|
|
3543
|
-
onActivate: activateSelectedPoint
|
|
4148
|
+
onActivate: activateSelectedPoint,
|
|
4149
|
+
preventTooltipScroll: tooltipPlacement === "below-axis"
|
|
3544
4150
|
});
|
|
3545
4151
|
const chartOptions = (0, react.useMemo)(() => {
|
|
3546
|
-
const { tickResolution, tickFormat, ...xAxisOptions } = options?.axis?.x ?? {};
|
|
3547
|
-
const formatter = tickFormat || getFormatter(dataSorted, tickResolution);
|
|
3548
4152
|
return {
|
|
3549
4153
|
axis: {
|
|
3550
|
-
x: {
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
4154
|
+
x: buildTimeAxisOptions({
|
|
4155
|
+
dataSorted,
|
|
4156
|
+
width,
|
|
4157
|
+
axisOptions: options?.axis?.x,
|
|
4158
|
+
scaleDomain: options?.xScale?.domain,
|
|
4159
|
+
zoomDomain: zoom.domain,
|
|
4160
|
+
formatting,
|
|
4161
|
+
isSeriesRendered: (series) => isSeriesVisible(series.label)
|
|
4162
|
+
}),
|
|
3557
4163
|
y: {
|
|
3558
4164
|
orientation: "left",
|
|
3559
4165
|
numTicks: 4,
|
|
@@ -3580,7 +4186,14 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
3580
4186
|
dataSorted,
|
|
3581
4187
|
width,
|
|
3582
4188
|
zoom.domain,
|
|
3583
|
-
stableYDomain
|
|
4189
|
+
stableYDomain,
|
|
4190
|
+
formatting,
|
|
4191
|
+
isSeriesVisible
|
|
4192
|
+
]);
|
|
4193
|
+
const bucketInfo = (0, react.useMemo)(() => getBucketInfo(dataSorted.filter((series) => isSeriesVisible(series.label)), options?.axis?.x?.tickResolution), [
|
|
4194
|
+
dataSorted,
|
|
4195
|
+
isSeriesVisible,
|
|
4196
|
+
options?.axis?.x?.tickResolution
|
|
3584
4197
|
]);
|
|
3585
4198
|
const tooltipRenderGlyph = (0, react.useMemo)(() => {
|
|
3586
4199
|
return (props) => {
|
|
@@ -3642,6 +4255,29 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
3642
4255
|
xAccessor: (d) => d?.date,
|
|
3643
4256
|
yAccessor: (d) => d?.value
|
|
3644
4257
|
};
|
|
4258
|
+
const resolvedTooltipStyle = (0, react.useMemo)(() => {
|
|
4259
|
+
if (renderTooltip !== renderDefaultTooltip || !tooltipStyle) return tooltipStyle;
|
|
4260
|
+
if (!tooltipStyle.color || tooltipStyle.background || tooltipStyle.backgroundColor) return tooltipStyle;
|
|
4261
|
+
return {
|
|
4262
|
+
backgroundColor: "var(--a8c-charts-color-tooltip-surface, rgb(0 0 0 / 85%))",
|
|
4263
|
+
...tooltipStyle
|
|
4264
|
+
};
|
|
4265
|
+
}, [renderTooltip, tooltipStyle]);
|
|
4266
|
+
const tooltipRenderer = (0, react.useMemo)(() => (params) => renderTooltip === renderDefaultTooltip ? renderDefaultTooltip({
|
|
4267
|
+
...params,
|
|
4268
|
+
bucketInfo
|
|
4269
|
+
}, {
|
|
4270
|
+
color: tooltipStyle?.color,
|
|
4271
|
+
background: tooltipStyle?.background,
|
|
4272
|
+
backgroundColor: tooltipStyle?.backgroundColor
|
|
4273
|
+
}) : renderTooltip({
|
|
4274
|
+
...params,
|
|
4275
|
+
bucketInfo
|
|
4276
|
+
}), [
|
|
4277
|
+
renderTooltip,
|
|
4278
|
+
bucketInfo,
|
|
4279
|
+
tooltipStyle
|
|
4280
|
+
]);
|
|
3645
4281
|
if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3646
4282
|
className: (0, clsx.default)("line-chart", line_chart_module_default["line-chart"]),
|
|
3647
4283
|
children: error
|
|
@@ -3690,7 +4326,7 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
3690
4326
|
onBlur: onChartBlur,
|
|
3691
4327
|
children: chartHeight > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3692
4328
|
ref: chartRef,
|
|
3693
|
-
|
|
4329
|
+
className: xy_plot_module_default["xy-plot"],
|
|
3694
4330
|
children: [zoomable && zoom.domain && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZoomResetButton, { onClick: zoom.reset }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_visx_xychart.XYChart, {
|
|
3695
4331
|
theme,
|
|
3696
4332
|
width,
|
|
@@ -3780,13 +4416,17 @@ const LineChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
3780
4416
|
withTooltips && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccessibleTooltip, {
|
|
3781
4417
|
detectBounds: true,
|
|
3782
4418
|
snapTooltipToDatumX: true,
|
|
4419
|
+
tooltipPlacement,
|
|
4420
|
+
style: resolvedTooltipStyle,
|
|
3783
4421
|
snapTooltipToDatumY: true,
|
|
3784
4422
|
showSeriesGlyphs: true,
|
|
3785
|
-
renderTooltip,
|
|
4423
|
+
renderTooltip: tooltipRenderer,
|
|
3786
4424
|
renderGlyph: tooltipRenderGlyph,
|
|
3787
4425
|
glyphStyle,
|
|
3788
4426
|
showVerticalCrosshair: withTooltipCrosshairs?.showVertical,
|
|
3789
4427
|
showHorizontalCrosshair: withTooltipCrosshairs?.showHorizontal,
|
|
4428
|
+
verticalCrosshairStyle: withTooltipCrosshairs?.verticalStyle,
|
|
4429
|
+
horizontalCrosshairStyle: withTooltipCrosshairs?.horizontalStyle,
|
|
3790
4430
|
selectedIndex,
|
|
3791
4431
|
tooltipRef,
|
|
3792
4432
|
keyboardFocusedClassName: line_chart_module_default["line-chart__tooltip--keyboard-focused"],
|
|
@@ -3920,12 +4560,11 @@ const HoverGlyphs = ({ visibleSeries, stacked, stackOffset, getElementStyles, st
|
|
|
3920
4560
|
};
|
|
3921
4561
|
//#endregion
|
|
3922
4562
|
//#region src/charts/area-chart/area-chart.tsx
|
|
3923
|
-
const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, stacked = true, stackOffset = "none", smoothing = true, curveType, fillOpacity, withStroke, renderTooltip = renderDefaultTooltip, animation, options = {}, onPointerDown, onPointerUp, onPointerMove, onPointerOut, zoomable = false, rescaleYOnVisibilityChange
|
|
4563
|
+
const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartId, width, height, className, margin, withTooltips = true, withTooltipCrosshairs, showLegend = false, legend = {}, stacked = true, stackOffset = "none", smoothing = true, curveType, fillOpacity, withStroke, renderTooltip = renderDefaultTooltip, animation, options = {}, onPointerDown, onPointerUp, onPointerMove, onPointerOut, zoomable = false, rescaleYOnVisibilityChange = true, defaultHiddenSeries, children, gridVisibility, gap = "md" }, ref) => {
|
|
3924
4564
|
const legendInteractive = legend.interactive ?? false;
|
|
3925
4565
|
const legendShape = legend.shape ?? "rect";
|
|
3926
4566
|
const legendPosition = legend.position ?? "bottom";
|
|
3927
|
-
const
|
|
3928
|
-
const providerTheme = useGlobalChartsTheme();
|
|
4567
|
+
const formatting = useChartFormatting();
|
|
3929
4568
|
const theme = useXYChartTheme(data);
|
|
3930
4569
|
const chartId = useChartId(providedChartId);
|
|
3931
4570
|
const hiddenSeries = useDefaultHiddenSeries(chartId, defaultHiddenSeries);
|
|
@@ -3975,7 +4614,7 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
3975
4614
|
totalPoints: dataSorted[0]?.data.length || 0
|
|
3976
4615
|
});
|
|
3977
4616
|
const fixedYDomain = (0, react.useMemo)(() => {
|
|
3978
|
-
if (
|
|
4617
|
+
if (rescaleYOnVisibilityChange || !dataSorted.length || !dataSorted[0].data.length || stacked && stackOffset !== "none") return;
|
|
3979
4618
|
if (stacked) {
|
|
3980
4619
|
const numPoints = Math.max(...dataSorted.map((s) => s.data.length));
|
|
3981
4620
|
let posMax = 0;
|
|
@@ -4009,20 +4648,19 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
4009
4648
|
dataSorted,
|
|
4010
4649
|
stacked,
|
|
4011
4650
|
stackOffset,
|
|
4012
|
-
|
|
4651
|
+
rescaleYOnVisibilityChange
|
|
4013
4652
|
]);
|
|
4014
4653
|
const chartOptions = (0, react.useMemo)(() => {
|
|
4015
|
-
const { tickResolution, tickFormat, ...xAxisOptions } = options?.axis?.x ?? {};
|
|
4016
|
-
const formatter = tickFormat || getFormatter(dataSorted, tickResolution);
|
|
4017
4654
|
return {
|
|
4018
4655
|
axis: {
|
|
4019
|
-
x: {
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4656
|
+
x: buildTimeAxisOptions({
|
|
4657
|
+
dataSorted,
|
|
4658
|
+
width,
|
|
4659
|
+
axisOptions: options?.axis?.x,
|
|
4660
|
+
scaleDomain: options?.xScale?.domain,
|
|
4661
|
+
zoomDomain: zoom.domain,
|
|
4662
|
+
formatting
|
|
4663
|
+
}),
|
|
4026
4664
|
y: {
|
|
4027
4665
|
orientation: "left",
|
|
4028
4666
|
numTicks: 4,
|
|
@@ -4050,7 +4688,8 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
4050
4688
|
width,
|
|
4051
4689
|
stacked,
|
|
4052
4690
|
fixedYDomain,
|
|
4053
|
-
zoom.domain
|
|
4691
|
+
zoom.domain,
|
|
4692
|
+
formatting
|
|
4054
4693
|
]);
|
|
4055
4694
|
const defaultMargin = useChartMargin(height, chartOptions, dataSorted, theme);
|
|
4056
4695
|
const error = validateData$3(dataSorted);
|
|
@@ -4086,9 +4725,17 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
4086
4725
|
};
|
|
4087
4726
|
const zeroYAccessor = (0, react.useCallback)(() => 0, []);
|
|
4088
4727
|
const visibleLabels = (0, react.useMemo)(() => new Set(seriesWithVisibility.filter((s) => s.isVisible).map((s) => s.series.label)), [seriesWithVisibility]);
|
|
4728
|
+
const bucketInfo = (0, react.useMemo)(() => getBucketInfo(dataSorted.filter((series) => visibleLabels.has(series.label)), options?.axis?.x?.tickResolution), [
|
|
4729
|
+
dataSorted,
|
|
4730
|
+
visibleLabels,
|
|
4731
|
+
options?.axis?.x?.tickResolution
|
|
4732
|
+
]);
|
|
4089
4733
|
const filteredRenderTooltip = (0, react.useCallback)((params) => {
|
|
4090
4734
|
const datumByKey = params?.tooltipData?.datumByKey;
|
|
4091
|
-
if (!datumByKey) return renderTooltip(
|
|
4735
|
+
if (!datumByKey) return renderTooltip({
|
|
4736
|
+
...params,
|
|
4737
|
+
bucketInfo
|
|
4738
|
+
});
|
|
4092
4739
|
const filtered = Object.fromEntries(Object.entries(datumByKey).filter(([key]) => visibleLabels.has(key)));
|
|
4093
4740
|
if (Object.keys(filtered).length === 0) return null;
|
|
4094
4741
|
const nearestDatum = params?.tooltipData?.nearestDatum;
|
|
@@ -4098,13 +4745,18 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
4098
4745
|
};
|
|
4099
4746
|
return renderTooltip({
|
|
4100
4747
|
...params,
|
|
4748
|
+
bucketInfo,
|
|
4101
4749
|
tooltipData: {
|
|
4102
4750
|
...params.tooltipData,
|
|
4103
4751
|
datumByKey: filtered,
|
|
4104
4752
|
nearestDatum: nextNearest
|
|
4105
4753
|
}
|
|
4106
4754
|
});
|
|
4107
|
-
}, [
|
|
4755
|
+
}, [
|
|
4756
|
+
renderTooltip,
|
|
4757
|
+
visibleLabels,
|
|
4758
|
+
bucketInfo
|
|
4759
|
+
]);
|
|
4108
4760
|
const resolvedFillOpacity = fillOpacity ?? (stacked ? .85 : .4);
|
|
4109
4761
|
const resolvedWithStroke = withStroke ?? !stacked;
|
|
4110
4762
|
if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -4179,7 +4831,7 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
4179
4831
|
onBlur: onChartBlur,
|
|
4180
4832
|
children: chartHeight > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4181
4833
|
ref: chartRef,
|
|
4182
|
-
|
|
4834
|
+
className: xy_plot_module_default["xy-plot"],
|
|
4183
4835
|
children: [zoomable && zoom.domain && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZoomResetButton, { onClick: zoom.reset }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_visx_xychart.XYChart, {
|
|
4184
4836
|
theme,
|
|
4185
4837
|
width,
|
|
@@ -4235,7 +4887,7 @@ const AreaChartInternal = (0, react.forwardRef)(({ data, chartId: providedChartI
|
|
|
4235
4887
|
stacked,
|
|
4236
4888
|
stackOffset,
|
|
4237
4889
|
getElementStyles,
|
|
4238
|
-
strokeColor: theme.backgroundColor
|
|
4890
|
+
strokeColor: theme.backgroundColor
|
|
4239
4891
|
})] }),
|
|
4240
4892
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AreaChartScalesRef, {
|
|
4241
4893
|
chartRef: internalChartRef,
|
|
@@ -4403,8 +5055,7 @@ const TOOLTIP_FORMAT_BY_RESOLUTION = {
|
|
|
4403
5055
|
year: "numeric",
|
|
4404
5056
|
month: "long",
|
|
4405
5057
|
day: "numeric",
|
|
4406
|
-
hour: "numeric"
|
|
4407
|
-
hour12: true
|
|
5058
|
+
hour: "numeric"
|
|
4408
5059
|
},
|
|
4409
5060
|
day: {
|
|
4410
5061
|
year: "numeric",
|
|
@@ -4423,16 +5074,16 @@ const TOOLTIP_FORMAT_BY_RESOLUTION = {
|
|
|
4423
5074
|
*
|
|
4424
5075
|
* @param data - Date-based series, already parsed and sorted by `useChartDataTransform`.
|
|
4425
5076
|
* @param tickResolution - Caller-declared bucket resolution, when known.
|
|
5077
|
+
* @param formatting - Host locale and time zone.
|
|
4426
5078
|
* @return Tooltip label formatter.
|
|
4427
5079
|
*/
|
|
4428
|
-
const getTooltipFormatter = (data, tickResolution) => {
|
|
4429
|
-
if (tickResolution === "week")
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
}));
|
|
5080
|
+
const getTooltipFormatter = (data, tickResolution, formatting) => {
|
|
5081
|
+
if (tickResolution === "week") {
|
|
5082
|
+
const formatDay = createDateFormatter(TOOLTIP_FORMAT_BY_RESOLUTION.day, formatting);
|
|
5083
|
+
return (timestamp) => (0, _wordpress_i18n.sprintf)((0, _wordpress_i18n.__)("Week of %s", "jetpack-charts"), formatDay(timestamp));
|
|
5084
|
+
}
|
|
4434
5085
|
const format = TOOLTIP_FORMAT_BY_RESOLUTION[getBucketResolution(data, tickResolution)] ?? TOOLTIP_FORMAT_BY_RESOLUTION.day;
|
|
4435
|
-
return (
|
|
5086
|
+
return createDateFormatter(format, formatting);
|
|
4436
5087
|
};
|
|
4437
5088
|
const identity = (label) => label;
|
|
4438
5089
|
const byBucket = (format) => (bucket) => typeof bucket === "string" ? bucket : format(Number(bucket));
|
|
@@ -4483,6 +5134,7 @@ const getGroupPadding = (scale) => {
|
|
|
4483
5134
|
*/
|
|
4484
5135
|
function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = ALL_RENDERED) {
|
|
4485
5136
|
const stableOptions = useDeepMemo(options);
|
|
5137
|
+
const formatting = useChartFormatting();
|
|
4486
5138
|
const axisConfig = (0, react.useMemo)(() => {
|
|
4487
5139
|
const { labelOverflow: xLabelOverflow, tickResolution: xTickResolution, tickFormat: xTickFormat, ...xAxisOptions } = stableOptions.axis?.x || {};
|
|
4488
5140
|
const { labelOverflow: yLabelOverflow, tickResolution: yTickResolution, tickFormat: yTickFormat, ...yAxisOptions } = stableOptions.axis?.y || {};
|
|
@@ -4509,9 +5161,9 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
|
|
|
4509
5161
|
zero: false
|
|
4510
5162
|
};
|
|
4511
5163
|
const hasLabels = Boolean(data?.[0]?.data?.[0]?.label);
|
|
4512
|
-
const timeTickFormatter = hasLabels ? null : getFormatter(data, tickResolution);
|
|
5164
|
+
const timeTickFormatter = hasLabels ? null : getFormatter(data, tickResolution, formatting);
|
|
4513
5165
|
const labelFormatter = timeTickFormatter ? byBucket(timeTickFormatter) : identity;
|
|
4514
|
-
const tooltipDatumFormatter = hasLabels ? labelFormatter : byBucket(getTooltipFormatter(data, tickResolution));
|
|
5166
|
+
const tooltipDatumFormatter = hasLabels ? labelFormatter : byBucket(getTooltipFormatter(data, tickResolution, formatting));
|
|
4515
5167
|
const valueFormatter = _automattic_number_formatters.formatNumberCompact;
|
|
4516
5168
|
const bandDomain = timeTickFormatter ? getBandDomain(data, isSeriesRendered) : null;
|
|
4517
5169
|
const valueAccessor = (d) => {
|
|
@@ -4547,7 +5199,8 @@ function useBarChartOptions(data, horizontal, options = {}, isSeriesRendered = A
|
|
|
4547
5199
|
}, [
|
|
4548
5200
|
data,
|
|
4549
5201
|
tickResolution,
|
|
4550
|
-
isSeriesRendered
|
|
5202
|
+
isSeriesRendered,
|
|
5203
|
+
formatting
|
|
4551
5204
|
]);
|
|
4552
5205
|
return (0, react.useMemo)(() => {
|
|
4553
5206
|
const { xTickFormat: defaultXTickFormat, yTickFormat: defaultYTickFormat, tooltipLabelFormatter: defaultTooltipLabelFormatter, xAccessor, yAccessor, gridVisibility, xScale: baseXScale, yScale: baseYScale } = defaultOptions[horizontal ? "horizontal" : "vertical"];
|
|
@@ -5056,6 +5709,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height, class
|
|
|
5056
5709
|
onBlur: onChartBlur,
|
|
5057
5710
|
children: chartHeight > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5058
5711
|
ref: chartRef,
|
|
5712
|
+
className: xy_plot_module_default["xy-plot"],
|
|
5059
5713
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_visx_xychart.XYChart, {
|
|
5060
5714
|
theme,
|
|
5061
5715
|
width,
|
|
@@ -5223,7 +5877,7 @@ const BarListChartInternal = ({ data, width, height, options = {}, margin = {
|
|
|
5223
5877
|
right: 20,
|
|
5224
5878
|
bottom: 0,
|
|
5225
5879
|
top: 0
|
|
5226
|
-
}, ...rest }) => {
|
|
5880
|
+
}, className, ...rest }) => {
|
|
5227
5881
|
const { getElementStyles } = useGlobalChartsContext();
|
|
5228
5882
|
const tintedData = (0, react.useMemo)(() => {
|
|
5229
5883
|
if (data.length > 1) return data;
|
|
@@ -5275,6 +5929,7 @@ const BarListChartInternal = ({ data, width, height, options = {}, margin = {
|
|
|
5275
5929
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BarChart, {
|
|
5276
5930
|
orientation: "horizontal",
|
|
5277
5931
|
gridVisibility: "none",
|
|
5932
|
+
className: (0, clsx.default)("bar-list-chart", className),
|
|
5278
5933
|
data: tintedData,
|
|
5279
5934
|
width,
|
|
5280
5935
|
height,
|
|
@@ -5311,6 +5966,9 @@ const BarListChartResponsive = withResponsive(BarListChart);
|
|
|
5311
5966
|
var conversion_funnel_chart_module_default = {
|
|
5312
5967
|
"bar-container": "a8ccharts-mGEVca-bar-container",
|
|
5313
5968
|
"change-indicator": "a8ccharts-mGEVca-change-indicator",
|
|
5969
|
+
"change-indicator--negative": "a8ccharts-mGEVca-change-indicator--negative",
|
|
5970
|
+
"change-indicator--positive": "a8ccharts-mGEVca-change-indicator--positive",
|
|
5971
|
+
"conversion-funnel-chart": "a8ccharts-mGEVca-conversion-funnel-chart",
|
|
5314
5972
|
"conversion-funnel-chart--loading": "a8ccharts-mGEVca-conversion-funnel-chart--loading",
|
|
5315
5973
|
"empty-state": "a8ccharts-mGEVca-empty-state",
|
|
5316
5974
|
"funnel-bar": "a8ccharts-mGEVca-funnel-bar",
|
|
@@ -5391,23 +6049,14 @@ const useFunnelSelection = (hideTooltip) => {
|
|
|
5391
6049
|
*/
|
|
5392
6050
|
const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loading = false, animation, className, chartId: providedChartId, height, style, renderStepLabel, renderStepRate, renderMainMetric, renderTooltip }) => {
|
|
5393
6051
|
const chartId = useChartId(providedChartId);
|
|
5394
|
-
const { conversionFunnelChart: conversionFunnelChartSettings } = useGlobalChartsTheme();
|
|
5395
6052
|
const { getElementStyles, isColorPaletteResolved } = useGlobalChartsContext();
|
|
5396
6053
|
const chartRef = (0, react.useRef)(null);
|
|
5397
6054
|
const selectedBarRef = (0, react.useRef)(null);
|
|
5398
6055
|
const [scopeNode, setScopeNode] = (0, react.useState)(null);
|
|
5399
6056
|
const { tooltipData, tooltipLeft, tooltipTop, tooltipOpen, showTooltip, hideTooltip } = (0, _visx_tooltip.useTooltip)();
|
|
5400
6057
|
const { handleBarClick, handleBarKeyDown, clearSelection, getStepState } = useFunnelSelection(hideTooltip);
|
|
5401
|
-
const
|
|
5402
|
-
detectBounds: true,
|
|
5403
|
-
scroll: true
|
|
5404
|
-
});
|
|
5405
|
-
const portalContainerRefRef = (0, react.useRef)(portalContainerRef);
|
|
5406
|
-
(0, react.useEffect)(() => {
|
|
5407
|
-
portalContainerRefRef.current = portalContainerRef;
|
|
5408
|
-
}, [portalContainerRef]);
|
|
6058
|
+
const standaloneScopeClass = useStandaloneScopeClass();
|
|
5409
6059
|
const setChartRef = (0, react.useCallback)((node) => {
|
|
5410
|
-
portalContainerRefRef.current(node);
|
|
5411
6060
|
chartRef.current = node;
|
|
5412
6061
|
setScopeNode(node);
|
|
5413
6062
|
}, []);
|
|
@@ -5424,30 +6073,22 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
|
|
|
5424
6073
|
});
|
|
5425
6074
|
}, [showTooltip]);
|
|
5426
6075
|
const getMouseTooltipCoords = (0, react.useCallback)((event) => {
|
|
5427
|
-
|
|
6076
|
+
const bounds = chartRef.current?.getBoundingClientRect();
|
|
6077
|
+
if (!bounds) return null;
|
|
5428
6078
|
return {
|
|
5429
|
-
x: event.clientX -
|
|
5430
|
-
y: event.clientY -
|
|
6079
|
+
x: event.clientX - bounds.left,
|
|
6080
|
+
y: event.clientY - bounds.top
|
|
5431
6081
|
};
|
|
5432
|
-
}, [
|
|
5433
|
-
containerBounds.width,
|
|
5434
|
-
containerBounds.height,
|
|
5435
|
-
containerBounds.left,
|
|
5436
|
-
containerBounds.top
|
|
5437
|
-
]);
|
|
6082
|
+
}, []);
|
|
5438
6083
|
const getKeyboardTooltipCoords = (0, react.useCallback)((event) => {
|
|
5439
|
-
|
|
6084
|
+
const bounds = chartRef.current?.getBoundingClientRect();
|
|
6085
|
+
if (!bounds) return null;
|
|
5440
6086
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
5441
6087
|
return {
|
|
5442
|
-
x: rect.left + rect.width / 2 -
|
|
5443
|
-
y: rect.top -
|
|
6088
|
+
x: rect.left + rect.width / 2 - bounds.left,
|
|
6089
|
+
y: rect.top - bounds.top
|
|
5444
6090
|
};
|
|
5445
|
-
}, [
|
|
5446
|
-
containerBounds.width,
|
|
5447
|
-
containerBounds.height,
|
|
5448
|
-
containerBounds.left,
|
|
5449
|
-
containerBounds.top
|
|
5450
|
-
]);
|
|
6091
|
+
}, []);
|
|
5451
6092
|
const handleStepInteraction = (0, react.useCallback)((step, event, interactionType) => {
|
|
5452
6093
|
selectedBarRef.current = event.currentTarget;
|
|
5453
6094
|
const { isClicked } = getStepState(step.id);
|
|
@@ -5508,19 +6149,14 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
|
|
|
5508
6149
|
};
|
|
5509
6150
|
}, [clearSelectionAndRef]);
|
|
5510
6151
|
const resolvedHeight = height ?? style?.height ?? "100%";
|
|
5511
|
-
const {
|
|
5512
|
-
const
|
|
5513
|
-
|
|
5514
|
-
overrideColor: primaryColor
|
|
5515
|
-
}) : { color: primaryColor || "#000000" };
|
|
5516
|
-
const changeColor = changeIndicator?.startsWith("+") ? positiveChangeColor : negativeChangeColor;
|
|
5517
|
-
const barBackgroundColor = backgroundColor || hexToRgba(barColor, .08) || "rgba(0, 0, 0, 0.08)";
|
|
6152
|
+
const { color: barColor } = getElementStyles ? getElementStyles({ index: 0 }) : { color: "#000000" };
|
|
6153
|
+
const isPositiveChange = changeIndicator?.startsWith("+");
|
|
6154
|
+
const changeColor = isPositiveChange ? CATALOG_POINTERS.trendUp : CATALOG_POINTERS.trendDown;
|
|
5518
6155
|
const renderDefaultMainMetric = () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5519
6156
|
className: conversion_funnel_chart_module_default["main-rate"],
|
|
5520
6157
|
children: formatPercentage(mainRate)
|
|
5521
6158
|
}), changeIndicator && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5522
|
-
className: conversion_funnel_chart_module_default["change-indicator"],
|
|
5523
|
-
style: { color: changeColor },
|
|
6159
|
+
className: (0, clsx.default)(conversion_funnel_chart_module_default["change-indicator"], conversion_funnel_chart_module_default[isPositiveChange ? "change-indicator--positive" : "change-indicator--negative"]),
|
|
5524
6160
|
children: changeIndicator
|
|
5525
6161
|
})] });
|
|
5526
6162
|
const renderDefaultTooltip = (step) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_wordpress_ui.Stack, {
|
|
@@ -5558,7 +6194,7 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
|
|
|
5558
6194
|
align: "center",
|
|
5559
6195
|
justify: "center",
|
|
5560
6196
|
ref: setScopeNode,
|
|
5561
|
-
className: (0, clsx.default)(conversion_funnel_chart_module_default["conversion-funnel-chart"], loading && conversion_funnel_chart_module_default["conversion-funnel-chart--loading"], className),
|
|
6197
|
+
className: (0, clsx.default)("conversion-funnel-chart", conversion_funnel_chart_module_default["conversion-funnel-chart"], loading && conversion_funnel_chart_module_default["conversion-funnel-chart--loading"], className),
|
|
5562
6198
|
style: {
|
|
5563
6199
|
...style,
|
|
5564
6200
|
height: resolvedHeight
|
|
@@ -5569,16 +6205,16 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
|
|
|
5569
6205
|
})
|
|
5570
6206
|
});
|
|
5571
6207
|
const maxRate = Math.max(...steps.map((step) => step.rate));
|
|
5572
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(
|
|
6208
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_wordpress_ui.Stack, {
|
|
5573
6209
|
direction: "column",
|
|
5574
6210
|
gap: "xl",
|
|
5575
6211
|
ref: setChartRef,
|
|
5576
|
-
className: (0, clsx.default)(conversion_funnel_chart_module_default["conversion-funnel-chart"], loading && conversion_funnel_chart_module_default["conversion-funnel-chart--loading"], className),
|
|
6212
|
+
className: (0, clsx.default)("conversion-funnel-chart", conversion_funnel_chart_module_default["conversion-funnel-chart"], loading && conversion_funnel_chart_module_default["conversion-funnel-chart--loading"], className),
|
|
5577
6213
|
style: {
|
|
5578
6214
|
...style,
|
|
5579
6215
|
height: resolvedHeight
|
|
5580
6216
|
},
|
|
5581
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ChartScopeContext.Provider, {
|
|
6217
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ChartScopeContext.Provider, {
|
|
5582
6218
|
value: scopeNode,
|
|
5583
6219
|
children: [renderMainMetric ? renderMainMetric({
|
|
5584
6220
|
mainRate,
|
|
@@ -5630,7 +6266,6 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
|
|
|
5630
6266
|
role: "button",
|
|
5631
6267
|
tabIndex: isBlurred ? -1 : 0,
|
|
5632
6268
|
"aria-label": step.label,
|
|
5633
|
-
style: { backgroundColor: barBackgroundColor },
|
|
5634
6269
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5635
6270
|
className: (0, clsx.default)(conversion_funnel_chart_module_default["funnel-bar"], { [conversion_funnel_chart_module_default["funnel-bar--animated"]]: animation && !loading && !prefersReducedMotion }),
|
|
5636
6271
|
style: {
|
|
@@ -5642,23 +6277,23 @@ const ConversionFunnelChartInternal = ({ mainRate, changeIndicator, steps, loadi
|
|
|
5642
6277
|
}, step.id);
|
|
5643
6278
|
})
|
|
5644
6279
|
})]
|
|
5645
|
-
})
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
}
|
|
5661
|
-
})
|
|
6280
|
+
}), tooltipOpen && tooltipData && (() => {
|
|
6281
|
+
const tooltipContent = renderTooltip ? renderTooltip({
|
|
6282
|
+
step: tooltipData,
|
|
6283
|
+
index: steps.findIndex((s) => s.id === tooltipData.id),
|
|
6284
|
+
top: tooltipTop,
|
|
6285
|
+
left: tooltipLeft,
|
|
6286
|
+
className: conversion_funnel_chart_module_default["tooltip-wrapper"]
|
|
6287
|
+
}) : renderDefaultTooltip(tooltipData);
|
|
6288
|
+
if (!tooltipContent) return null;
|
|
6289
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BoundedTooltip, {
|
|
6290
|
+
top: tooltipTop,
|
|
6291
|
+
left: tooltipLeft,
|
|
6292
|
+
className: (0, clsx.default)(standaloneScopeClass, conversion_funnel_chart_module_default["tooltip-wrapper"]),
|
|
6293
|
+
children: tooltipContent
|
|
6294
|
+
});
|
|
6295
|
+
})()]
|
|
6296
|
+
});
|
|
5662
6297
|
};
|
|
5663
6298
|
/**
|
|
5664
6299
|
* ConversionFunnelChart component with provider wrapper
|
|
@@ -5804,7 +6439,7 @@ function normalizeGeoChartError(eventArgs) {
|
|
|
5804
6439
|
* @return A React component displaying an interactive map with data visualization
|
|
5805
6440
|
*/
|
|
5806
6441
|
const GeoChartInternal = ({ className, data, width, height, region = "world", resolution = "countries", onError, renderPlaceholder }) => {
|
|
5807
|
-
const { getElementStyles
|
|
6442
|
+
const { getElementStyles } = useGlobalChartsContext();
|
|
5808
6443
|
const scopeElement = useChartScopeElement();
|
|
5809
6444
|
const containerRef = (0, react.useRef)(null);
|
|
5810
6445
|
const reportedErrorIdsRef = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
@@ -5838,8 +6473,8 @@ const GeoChartInternal = ({ className, data, width, height, region = "world", re
|
|
|
5838
6473
|
});
|
|
5839
6474
|
const fullColorHex = getElementStyles({ index: 0 }).color;
|
|
5840
6475
|
const lightColorHex = lightenHexColor(fullColorHex, .8);
|
|
5841
|
-
const backgroundColorHex = normalizeColorToHex(
|
|
5842
|
-
const defaultFillColorHex = normalizeColorToHex(
|
|
6476
|
+
const backgroundColorHex = normalizeColorToHex(CATALOG_POINTERS.background, scopeElement, resolveCssVariable) || DEFAULT_BACKGROUND_COLOR;
|
|
6477
|
+
const defaultFillColorHex = normalizeColorToHex(CATALOG_POINTERS.surfaceSecondary, scopeElement, resolveCssVariable) || DEFAULT_FEATURE_FILL_COLOR;
|
|
5843
6478
|
const sanitizedData = (0, react.useMemo)(() => {
|
|
5844
6479
|
if (data.length === 0) return {
|
|
5845
6480
|
data,
|
|
@@ -5900,8 +6535,7 @@ const GeoChartInternal = ({ className, data, width, height, region = "world", re
|
|
|
5900
6535
|
className: (0, clsx.default)("geo-chart", geo_chart_module_default.container, className),
|
|
5901
6536
|
style: {
|
|
5902
6537
|
width,
|
|
5903
|
-
height
|
|
5904
|
-
backgroundColor
|
|
6538
|
+
height
|
|
5905
6539
|
},
|
|
5906
6540
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_google_charts.Chart, {
|
|
5907
6541
|
chartType: "GeoChart",
|
|
@@ -5945,12 +6579,17 @@ var heatmap_chart_module_default = {
|
|
|
5945
6579
|
"heatmap-chart__cell--placeholder": "a8ccharts-O3YMOW-heatmap-chart__cell--placeholder",
|
|
5946
6580
|
"heatmap-chart__cell--selected": "a8ccharts-O3YMOW-heatmap-chart__cell--selected",
|
|
5947
6581
|
"heatmap-chart__cell--strong": "a8ccharts-O3YMOW-heatmap-chart__cell--strong",
|
|
6582
|
+
"heatmap-chart__cell--summary": "a8ccharts-O3YMOW-heatmap-chart__cell--summary",
|
|
5948
6583
|
"heatmap-chart__cell-value": "a8ccharts-O3YMOW-heatmap-chart__cell-value",
|
|
5949
6584
|
"heatmap-chart__col-label": "a8ccharts-O3YMOW-heatmap-chart__col-label",
|
|
6585
|
+
"heatmap-chart__col-label--summary": "a8ccharts-O3YMOW-heatmap-chart__col-label--summary",
|
|
5950
6586
|
"heatmap-chart__empty": "a8ccharts-O3YMOW-heatmap-chart__empty",
|
|
6587
|
+
"heatmap-chart__gap-end": "a8ccharts-O3YMOW-heatmap-chart__gap-end",
|
|
6588
|
+
"heatmap-chart__gap-start": "a8ccharts-O3YMOW-heatmap-chart__gap-start",
|
|
5951
6589
|
"heatmap-chart__grid": "a8ccharts-O3YMOW-heatmap-chart__grid",
|
|
5952
6590
|
"heatmap-chart__grid--compact": "a8ccharts-O3YMOW-heatmap-chart__grid--compact",
|
|
5953
6591
|
"heatmap-chart__grid--height-capped": "a8ccharts-O3YMOW-heatmap-chart__grid--height-capped",
|
|
6592
|
+
"heatmap-chart__legend-label": "a8ccharts-O3YMOW-heatmap-chart__legend-label",
|
|
5954
6593
|
"heatmap-chart__legend-swatch": "a8ccharts-O3YMOW-heatmap-chart__legend-swatch",
|
|
5955
6594
|
"heatmap-chart__row": "a8ccharts-O3YMOW-heatmap-chart__row",
|
|
5956
6595
|
"heatmap-chart__row-label": "a8ccharts-O3YMOW-heatmap-chart__row-label",
|
|
@@ -5960,17 +6599,21 @@ var heatmap_chart_module_default = {
|
|
|
5960
6599
|
//#region src/charts/heatmap-chart/private/use-heatmap-colors.ts
|
|
5961
6600
|
const isPresent = (value) => value !== null && value !== void 0 && !isNaN(value);
|
|
5962
6601
|
/**
|
|
5963
|
-
* Get the min and max values from heatmap data, ignoring null/NaN.
|
|
6602
|
+
* Get the min and max values from heatmap data, ignoring null/NaN. Summary
|
|
6603
|
+
* columns stay out: a roll-up on the scale would flatten every real cell.
|
|
5964
6604
|
* @param data - The heatmap columns
|
|
5965
6605
|
* @return Tuple of [min, max] values
|
|
5966
6606
|
*/
|
|
5967
6607
|
const getValueExtent = (data) => {
|
|
5968
6608
|
let min = Infinity;
|
|
5969
6609
|
let max = -Infinity;
|
|
5970
|
-
for (const column of data)
|
|
5971
|
-
if (
|
|
5972
|
-
|
|
5973
|
-
|
|
6610
|
+
for (const column of data) {
|
|
6611
|
+
if (column.summary) continue;
|
|
6612
|
+
for (const cell of column.data) {
|
|
6613
|
+
if (!isPresent(cell.value)) continue;
|
|
6614
|
+
if (cell.value < min) min = cell.value;
|
|
6615
|
+
if (cell.value > max) max = cell.value;
|
|
6616
|
+
}
|
|
5974
6617
|
}
|
|
5975
6618
|
if (min === Infinity) return [0, 0];
|
|
5976
6619
|
return [min, max];
|
|
@@ -5991,6 +6634,119 @@ const getNormalizedValue = (value, extent) => {
|
|
|
5991
6634
|
return Math.min(1, Math.max(0, (value - min) / (max - min)));
|
|
5992
6635
|
};
|
|
5993
6636
|
//#endregion
|
|
6637
|
+
//#region src/charts/heatmap-chart/private/civil-day.ts
|
|
6638
|
+
const DAY_MS = 864e5;
|
|
6639
|
+
const KEY_OPTIONS = {
|
|
6640
|
+
calendar: "gregory",
|
|
6641
|
+
numberingSystem: "latn",
|
|
6642
|
+
year: "numeric",
|
|
6643
|
+
month: "2-digit",
|
|
6644
|
+
day: "2-digit"
|
|
6645
|
+
};
|
|
6646
|
+
const DATE_PREFIX = /^(\d{4})-(\d{2})-(\d{2})/;
|
|
6647
|
+
const pad = (value, length) => String(value).padStart(length, "0");
|
|
6648
|
+
/**
|
|
6649
|
+
* The day a UTC proxy stands for.
|
|
6650
|
+
*
|
|
6651
|
+
* @param date - UTC proxy.
|
|
6652
|
+
* @return Its day key.
|
|
6653
|
+
*/
|
|
6654
|
+
const civilKey = (date) => `${pad(date.getUTCFullYear(), 4)}-${pad(date.getUTCMonth() + 1, 2)}-${pad(date.getUTCDate(), 2)}`;
|
|
6655
|
+
/**
|
|
6656
|
+
* A day key as a UTC-midnight proxy. UTC has no DST, so every step below is exactly
|
|
6657
|
+
* `DAY_MS`.
|
|
6658
|
+
*
|
|
6659
|
+
* @param key - Day key.
|
|
6660
|
+
* @return The proxy, or null when the key names no real day.
|
|
6661
|
+
*/
|
|
6662
|
+
const civilDate = (key) => {
|
|
6663
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(key);
|
|
6664
|
+
if (!match) return null;
|
|
6665
|
+
const date = /* @__PURE__ */ new Date(0);
|
|
6666
|
+
date.setUTCFullYear(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
|
|
6667
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
6668
|
+
return civilKey(date) === key ? date : null;
|
|
6669
|
+
};
|
|
6670
|
+
/**
|
|
6671
|
+
* The day a written date names, ignoring any time and zone it carries.
|
|
6672
|
+
*
|
|
6673
|
+
* @param text - `yyyy-MM-dd`, optionally followed by a time.
|
|
6674
|
+
* @return Its day key, or null.
|
|
6675
|
+
*/
|
|
6676
|
+
const writtenDayKey = (text) => {
|
|
6677
|
+
const match = DATE_PREFIX.exec(text);
|
|
6678
|
+
if (!match) return null;
|
|
6679
|
+
const key = `${match[1]}-${match[2]}-${match[3]}`;
|
|
6680
|
+
return civilDate(key) ? key : null;
|
|
6681
|
+
};
|
|
6682
|
+
/**
|
|
6683
|
+
* Reads the day an instant falls on in a zone.
|
|
6684
|
+
*
|
|
6685
|
+
* @param timeZone - IANA zone name; absent leaves the runtime's own.
|
|
6686
|
+
* @return Reader taking a `Date`, null for a day no key can hold.
|
|
6687
|
+
*/
|
|
6688
|
+
const instantDayReader = (timeZone) => {
|
|
6689
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
6690
|
+
...KEY_OPTIONS,
|
|
6691
|
+
timeZone
|
|
6692
|
+
});
|
|
6693
|
+
return (date) => {
|
|
6694
|
+
const parts = formatter.formatToParts(date);
|
|
6695
|
+
const read = (type) => parts.find((part) => part.type === type)?.value ?? "";
|
|
6696
|
+
const key = `${pad(Number(read("year")), 4)}-${read("month")}-${read("day")}`;
|
|
6697
|
+
return civilDate(key) ? key : null;
|
|
6698
|
+
};
|
|
6699
|
+
};
|
|
6700
|
+
/**
|
|
6701
|
+
* The day a series point belongs to.
|
|
6702
|
+
*
|
|
6703
|
+
* A `Date`, or a string carrying `Z` or an offset, is an instant read in the host's
|
|
6704
|
+
* zone. Anything else is a wall clock the host wrote, so its date part is the day.
|
|
6705
|
+
*
|
|
6706
|
+
* @param point - Series point.
|
|
6707
|
+
* @param readInstant - Reader from `instantDayReader`.
|
|
6708
|
+
* @return Its day key, or null when the point carries no usable date.
|
|
6709
|
+
*/
|
|
6710
|
+
const pointDayKey = (point, readInstant) => {
|
|
6711
|
+
if (point.date instanceof Date && !isNaN(point.date.getTime())) {
|
|
6712
|
+
const key = readInstant(point.date);
|
|
6713
|
+
if (key) return key;
|
|
6714
|
+
}
|
|
6715
|
+
const text = point.dateString;
|
|
6716
|
+
if (!text) return null;
|
|
6717
|
+
if (!hasTimezone(text)) return writtenDayKey(text);
|
|
6718
|
+
const parsed = (0, date_fns.parseISO)(text);
|
|
6719
|
+
return isNaN(parsed.getTime()) ? null : readInstant(parsed);
|
|
6720
|
+
};
|
|
6721
|
+
/**
|
|
6722
|
+
* Steps a proxy by whole days.
|
|
6723
|
+
*
|
|
6724
|
+
* @param date - UTC proxy.
|
|
6725
|
+
* @param days - Days to add; may be negative.
|
|
6726
|
+
* @return The shifted proxy.
|
|
6727
|
+
*/
|
|
6728
|
+
const addCivilDays = (date, days) => new Date(date.getTime() + days * DAY_MS);
|
|
6729
|
+
/**
|
|
6730
|
+
* Rounds a proxy down to its week.
|
|
6731
|
+
*
|
|
6732
|
+
* @param date - UTC proxy.
|
|
6733
|
+
* @param weekStartsOn - 0 for Sunday, 1 for Monday.
|
|
6734
|
+
* @return The proxy for that week's first day.
|
|
6735
|
+
*/
|
|
6736
|
+
const startOfCivilWeek = (date, weekStartsOn) => {
|
|
6737
|
+
const daysSinceWeekStart = (date.getUTCDay() - weekStartsOn + 7) % 7;
|
|
6738
|
+
return addCivilDays(date, -daysSinceWeekStart);
|
|
6739
|
+
};
|
|
6740
|
+
/**
|
|
6741
|
+
* Counts the columns a span needs.
|
|
6742
|
+
*
|
|
6743
|
+
* @param from - Earlier UTC proxy.
|
|
6744
|
+
* @param to - Later UTC proxy.
|
|
6745
|
+
* @param weekStartsOn - 0 for Sunday, 1 for Monday.
|
|
6746
|
+
* @return Columns needed to cover the span, inclusive.
|
|
6747
|
+
*/
|
|
6748
|
+
const civilWeekSpan = (from, to, weekStartsOn) => Math.floor((startOfCivilWeek(to, weekStartsOn).getTime() - startOfCivilWeek(from, weekStartsOn).getTime()) / (7 * DAY_MS)) + 1;
|
|
6749
|
+
//#endregion
|
|
5994
6750
|
//#region src/charts/heatmap-chart/private/build-calendar-data.ts
|
|
5995
6751
|
/** Rows that get a weekday label (Mon, Wed, Fri with a Monday week start). */
|
|
5996
6752
|
const LABELLED_ROWS = [
|
|
@@ -6005,69 +6761,88 @@ const LABELLED_ROWS = [
|
|
|
6005
6761
|
* @param bound - Requested bound as `yyyy-MM-dd`, if any.
|
|
6006
6762
|
* @param fallback - The series' own bound on this side.
|
|
6007
6763
|
* @param direction - Which way the bound is allowed to move.
|
|
6008
|
-
* @return The
|
|
6764
|
+
* @return The day key to draw to.
|
|
6009
6765
|
*/
|
|
6010
6766
|
const widenTo = (bound, fallback, direction) => {
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
return parsed > fallback ? parsed : fallback;
|
|
6016
|
-
};
|
|
6017
|
-
const toDate = (point) => {
|
|
6018
|
-
if (point.date instanceof Date && !isNaN(point.date.getTime())) return point.date;
|
|
6019
|
-
if (point.dateString) {
|
|
6020
|
-
const parsed = (0, date_fns.parseISO)(point.dateString);
|
|
6021
|
-
if (!isNaN(parsed.getTime())) return parsed;
|
|
6767
|
+
const key = bound ? writtenDayKey(bound) : null;
|
|
6768
|
+
if (!key) {
|
|
6769
|
+
if (bound) warnOnce(`heatmap:gridSpan:${bound}`, `gridSpan.${direction === "earlier" ? "start" : "end"} ${JSON.stringify(bound)} is not a \`yyyy-MM-dd\` day, so the grid is drawn over the series' own span.`);
|
|
6770
|
+
return fallback;
|
|
6022
6771
|
}
|
|
6023
|
-
return
|
|
6772
|
+
if (direction === "earlier") return key < fallback ? key : fallback;
|
|
6773
|
+
return key > fallback ? key : fallback;
|
|
6024
6774
|
};
|
|
6775
|
+
/**
|
|
6776
|
+
* Lay a day-bucketed series out as calendar columns.
|
|
6777
|
+
*
|
|
6778
|
+
* @param series - Points to bucket.
|
|
6779
|
+
* @param options - Grid shape, plus the locale and zone to read days in.
|
|
6780
|
+
* @return Columns and row labels for `HeatmapChart`.
|
|
6781
|
+
*/
|
|
6025
6782
|
const buildCalendarHeatmapData = (series, options = {}) => {
|
|
6026
6783
|
const weekStartsOn = options.weekStartsOn ?? 1;
|
|
6027
6784
|
const hideOutOfRangeDays = options.hideOutOfRangeDays ?? true;
|
|
6028
|
-
const
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
})
|
|
6032
|
-
|
|
6785
|
+
const { locale, timeZone } = sanitizeFormatting({
|
|
6786
|
+
locale: options.locale,
|
|
6787
|
+
timeZone: options.timeZone
|
|
6788
|
+
});
|
|
6789
|
+
const readInstant = instantDayReader(timeZone);
|
|
6790
|
+
const valueByDay = /* @__PURE__ */ new Map();
|
|
6791
|
+
let minDayKey;
|
|
6792
|
+
let maxDayKey;
|
|
6793
|
+
for (const point of series) {
|
|
6794
|
+
const key = pointDayKey(point, readInstant);
|
|
6795
|
+
if (!key) {
|
|
6796
|
+
const offending = point.dateString ?? (point.date && String(point.date));
|
|
6797
|
+
warnOnce(`heatmap:unreadableDate:${offending}`, offending ? `${JSON.stringify(offending)} is not a day this can read, so its point is left out of the calendar. A \`dateString\` must start \`yyyy-MM-dd\`.` : "A point carries neither `date` nor `dateString`, so it is left out of the calendar.");
|
|
6798
|
+
continue;
|
|
6799
|
+
}
|
|
6800
|
+
valueByDay.set(key, point.value);
|
|
6801
|
+
if (!minDayKey || key < minDayKey) minDayKey = key;
|
|
6802
|
+
if (!maxDayKey || key > maxDayKey) maxDayKey = key;
|
|
6803
|
+
}
|
|
6804
|
+
if (!minDayKey || !maxDayKey) return {
|
|
6033
6805
|
data: [],
|
|
6034
6806
|
rowLabels: []
|
|
6035
6807
|
};
|
|
6036
|
-
const
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
}
|
|
6044
|
-
const
|
|
6045
|
-
const
|
|
6046
|
-
const
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
const
|
|
6808
|
+
const requestedMinDayKey = widenTo(options.gridSpan?.start, minDayKey, "earlier");
|
|
6809
|
+
const gridMaxDayKey = widenTo(options.gridSpan?.end, maxDayKey, "later");
|
|
6810
|
+
const requestedMinDate = civilDate(requestedMinDayKey);
|
|
6811
|
+
const gridMaxDate = civilDate(gridMaxDayKey);
|
|
6812
|
+
const labelFormatting = {
|
|
6813
|
+
locale,
|
|
6814
|
+
timeZone: "UTC"
|
|
6815
|
+
};
|
|
6816
|
+
const formatWeekday = createDateFormatter({ weekday: "short" }, labelFormatting);
|
|
6817
|
+
const formatMonth = createDateFormatter({ month: "short" }, labelFormatting);
|
|
6818
|
+
const formatDay = createDateFormatter({
|
|
6819
|
+
weekday: "short",
|
|
6820
|
+
month: "short",
|
|
6821
|
+
day: "numeric",
|
|
6822
|
+
year: "numeric"
|
|
6823
|
+
}, labelFormatting);
|
|
6824
|
+
const gridStart = startOfCivilWeek(requestedMinDate, weekStartsOn);
|
|
6825
|
+
const gridMinDayKey = requestedMinDayKey < minDayKey ? civilKey(gridStart) : requestedMinDayKey;
|
|
6826
|
+
const weekCount = civilWeekSpan(gridStart, gridMaxDate, weekStartsOn);
|
|
6827
|
+
const rowLabels = Array.from({ length: 7 }, (_, row) => LABELLED_ROWS.includes(row) ? formatWeekday(addCivilDays(gridStart, row)) : "");
|
|
6053
6828
|
const MIN_FIRST_MONTH_WEEKS = 2;
|
|
6054
|
-
const firstMonth = gridStart.
|
|
6829
|
+
const firstMonth = gridStart.getUTCMonth();
|
|
6055
6830
|
let firstMonthWeeks = 0;
|
|
6056
|
-
while (firstMonthWeeks < weekCount && (
|
|
6831
|
+
while (firstMonthWeeks < weekCount && addCivilDays(gridStart, firstMonthWeeks * 7).getUTCMonth() === firstMonth) firstMonthWeeks++;
|
|
6057
6832
|
const showFirstMonthLabel = !(firstMonthWeeks < weekCount) || firstMonthWeeks >= MIN_FIRST_MONTH_WEEKS;
|
|
6058
6833
|
const data = [];
|
|
6059
6834
|
let previousMonth = -1;
|
|
6060
6835
|
for (let week = 0; week < weekCount; week++) {
|
|
6061
|
-
const columnStart = (
|
|
6062
|
-
const month = columnStart.
|
|
6063
|
-
const label = month !== previousMonth && (week !== 0 || showFirstMonthLabel) ? (
|
|
6836
|
+
const columnStart = addCivilDays(gridStart, week * 7);
|
|
6837
|
+
const month = columnStart.getUTCMonth();
|
|
6838
|
+
const label = month !== previousMonth && (week !== 0 || showFirstMonthLabel) ? formatMonth(columnStart) : "";
|
|
6064
6839
|
previousMonth = month;
|
|
6065
6840
|
const cells = [];
|
|
6066
6841
|
for (let row = 0; row < 7; row++) {
|
|
6067
|
-
const day = (
|
|
6068
|
-
const key = (
|
|
6842
|
+
const day = addCivilDays(gridStart, week * 7 + row);
|
|
6843
|
+
const key = civilKey(day);
|
|
6069
6844
|
const cell = {
|
|
6070
|
-
label: (
|
|
6845
|
+
label: formatDay(day),
|
|
6071
6846
|
value: valueByDay.has(key) ? valueByDay.get(key) : null
|
|
6072
6847
|
};
|
|
6073
6848
|
if (key < gridMinDayKey || key > gridMaxDayKey) {
|
|
@@ -6093,7 +6868,7 @@ const HeatmapContext = (0, react.createContext)(null);
|
|
|
6093
6868
|
//#region src/charts/heatmap-chart/private/heatmap-legend.tsx
|
|
6094
6869
|
const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
|
|
6095
6870
|
const context = (0, react.useContext)(HeatmapContext);
|
|
6096
|
-
const { legend
|
|
6871
|
+
const { legend } = useGlobalChartsTheme();
|
|
6097
6872
|
if (!context) return null;
|
|
6098
6873
|
const { primaryColorHex } = context;
|
|
6099
6874
|
const labelStyle = legend.labelStyles;
|
|
@@ -6104,6 +6879,7 @@ const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
|
|
|
6104
6879
|
children: [
|
|
6105
6880
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Text, {
|
|
6106
6881
|
variant: "body-sm",
|
|
6882
|
+
className: heatmap_chart_module_default["heatmap-chart__legend-label"],
|
|
6107
6883
|
style: labelStyle,
|
|
6108
6884
|
children: lessLabel ?? (0, _wordpress_i18n.__)("Less", "jetpack-charts")
|
|
6109
6885
|
}),
|
|
@@ -6117,7 +6893,6 @@ const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
|
|
|
6117
6893
|
className: heatmap_chart_module_default["heatmap-chart__legend-swatch"],
|
|
6118
6894
|
style: {
|
|
6119
6895
|
"--a8c-charts-color-heatmap-primary": primaryColorHex,
|
|
6120
|
-
"--a8c-charts-color-heatmap-background": backgroundColor,
|
|
6121
6896
|
"--a8c-charts-heatmap-cell-intensity": intensity
|
|
6122
6897
|
}
|
|
6123
6898
|
}, index);
|
|
@@ -6125,6 +6900,7 @@ const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
|
|
|
6125
6900
|
}),
|
|
6126
6901
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Text, {
|
|
6127
6902
|
variant: "body-sm",
|
|
6903
|
+
className: heatmap_chart_module_default["heatmap-chart__legend-label"],
|
|
6128
6904
|
style: labelStyle,
|
|
6129
6905
|
children: moreLabel ?? (0, _wordpress_i18n.__)("More", "jetpack-charts")
|
|
6130
6906
|
})
|
|
@@ -6134,7 +6910,8 @@ const HeatmapLegend = ({ steps = 5, lessLabel, moreLabel }) => {
|
|
|
6134
6910
|
//#endregion
|
|
6135
6911
|
//#region src/charts/heatmap-chart/heatmap-chart.tsx
|
|
6136
6912
|
const CELL_MIX_FLOOR = .15;
|
|
6137
|
-
const
|
|
6913
|
+
const NO_ROW_LABELS = [];
|
|
6914
|
+
const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, height = 0, className, compact = false, showValues, maxCellWidth, maxCellHeight, minCellWidth, minCellHeight, rowLabels = NO_ROW_LABELS, primaryColor, gap = "md", withTooltips = false, renderTooltip, children }) => {
|
|
6138
6915
|
const chartId = useChartId(providedChartId);
|
|
6139
6916
|
const { getElementStyles, theme } = useGlobalChartsContext();
|
|
6140
6917
|
const scopeElement = useChartScopeElement();
|
|
@@ -6142,17 +6919,14 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
|
|
|
6142
6919
|
const { nonLegendChildren } = useChartChildren(children, "HeatmapChart");
|
|
6143
6920
|
const [selectedIndex, setSelectedIndex] = (0, react.useState)();
|
|
6144
6921
|
const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, showTooltip, hideTooltip } = (0, _visx_tooltip.useTooltip)();
|
|
6145
|
-
const
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
});
|
|
6149
|
-
const containerBoundsRef = (0, react.useRef)(containerBounds);
|
|
6150
|
-
containerBoundsRef.current = containerBounds;
|
|
6922
|
+
const standaloneScopeClass = useStandaloneScopeClass();
|
|
6923
|
+
const containerRef = (0, react.useRef)(null);
|
|
6924
|
+
const getTooltipOrigin = (0, react.useCallback)(() => containerRef.current?.closest(`[data-chart-id="heatmap-chart-${chartId}"]`)?.getBoundingClientRect() ?? null, [chartId]);
|
|
6151
6925
|
const { color: primaryColorHex } = getElementStyles({
|
|
6152
6926
|
index: 0,
|
|
6153
|
-
overrideColor: primaryColor
|
|
6927
|
+
overrideColor: primaryColor
|
|
6154
6928
|
});
|
|
6155
|
-
const chartBackgroundHex = normalizeColorToHex(
|
|
6929
|
+
const chartBackgroundHex = normalizeColorToHex(CATALOG_POINTERS.background, scopeElement, resolveCssVariable);
|
|
6156
6930
|
const primaryHex = normalizeColorToHex(primaryColorHex);
|
|
6157
6931
|
const cellHasLightText = (intensity) => isValidHexColor(primaryHex) && isValidHexColor(chartBackgroundHex) && prefersLightText(mixHexColors(primaryHex, chartBackgroundHex, 1 - (CELL_MIX_FLOOR + .85 * intensity)));
|
|
6158
6932
|
const extent = (0, react.useMemo)(() => getValueExtent(data), [data]);
|
|
@@ -6228,19 +7002,21 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
|
|
|
6228
7002
|
]);
|
|
6229
7003
|
const handleCellMouseMove = (0, react.useCallback)((event) => {
|
|
6230
7004
|
if (!withTooltips) return;
|
|
7005
|
+
const origin = getTooltipOrigin();
|
|
7006
|
+
if (!origin) return;
|
|
6231
7007
|
const target = event.currentTarget;
|
|
6232
7008
|
const columnIndex = Number(target.dataset.column);
|
|
6233
7009
|
const rowIndex = Number(target.dataset.row);
|
|
6234
|
-
const bounds = containerBoundsRef.current;
|
|
6235
7010
|
showTooltip({
|
|
6236
|
-
tooltipLeft: event.clientX -
|
|
6237
|
-
tooltipTop: event.clientY -
|
|
7011
|
+
tooltipLeft: event.clientX - origin.left,
|
|
7012
|
+
tooltipTop: event.clientY - origin.top,
|
|
6238
7013
|
tooltipData: buildTooltipData(columnIndex, rowIndex)
|
|
6239
7014
|
});
|
|
6240
7015
|
}, [
|
|
6241
7016
|
withTooltips,
|
|
6242
7017
|
showTooltip,
|
|
6243
|
-
buildTooltipData
|
|
7018
|
+
buildTooltipData,
|
|
7019
|
+
getTooltipOrigin
|
|
6244
7020
|
]);
|
|
6245
7021
|
const handleCellMouseLeave = (0, react.useCallback)(() => {
|
|
6246
7022
|
if (withTooltips && selectedIndex === void 0) hideTooltip();
|
|
@@ -6251,13 +7027,14 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
|
|
|
6251
7027
|
]);
|
|
6252
7028
|
(0, react.useEffect)(() => {
|
|
6253
7029
|
if (!withTooltips || selectedIndex === void 0) return;
|
|
7030
|
+
const origin = getTooltipOrigin();
|
|
7031
|
+
if (!origin) return;
|
|
6254
7032
|
const col = Math.floor(selectedIndex / rows);
|
|
6255
7033
|
const row = selectedIndex % rows;
|
|
6256
7034
|
const rect = (typeof document !== "undefined" ? document.getElementById(`${chartId}-cell-${col}-${row}`) : null)?.getBoundingClientRect();
|
|
6257
|
-
const bounds = containerBoundsRef.current;
|
|
6258
7035
|
showTooltip({
|
|
6259
|
-
tooltipLeft: rect ? rect.left + rect.width / 2 -
|
|
6260
|
-
tooltipTop: rect ? rect.top + rect.height / 2 -
|
|
7036
|
+
tooltipLeft: rect ? rect.left + rect.width / 2 - origin.left : 0,
|
|
7037
|
+
tooltipTop: rect ? rect.top + rect.height / 2 - origin.top : 0,
|
|
6261
7038
|
tooltipData: buildTooltipData(col, row)
|
|
6262
7039
|
});
|
|
6263
7040
|
}, [
|
|
@@ -6266,7 +7043,8 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
|
|
|
6266
7043
|
rows,
|
|
6267
7044
|
chartId,
|
|
6268
7045
|
buildTooltipData,
|
|
6269
|
-
showTooltip
|
|
7046
|
+
showTooltip,
|
|
7047
|
+
getTooltipOrigin
|
|
6270
7048
|
]);
|
|
6271
7049
|
const defaultRenderTooltip = (0, react.useCallback)((info) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: info.cellLabel || `${info.columnLabel ?? ""} ${info.rowLabel ?? ""}`.trim() }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: info.value === null ? (0, _wordpress_i18n.__)("No data", "jetpack-charts") : (0, _automattic_number_formatters.formatNumber)(info.value) })] }), []);
|
|
6272
7050
|
if (!columns || !rows) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Center, {
|
|
@@ -6284,14 +7062,20 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
|
|
|
6284
7062
|
const rowTrack = compact ? "var(--a8c-charts-dimension-heatmap-cell-size)" : `minmax(${minCellHeight ?? 0}px, ${maxCellHeight ? `${maxCellHeight}px` : "1fr"})`;
|
|
6285
7063
|
const gridStyle = {
|
|
6286
7064
|
"--a8c-charts-color-heatmap-primary": primaryColorHex,
|
|
6287
|
-
"
|
|
6288
|
-
gridTemplateColumns: `auto repeat(${columns}, ${columnTrack})`,
|
|
7065
|
+
gridTemplateColumns: `auto ${data.some((column) => column.summary) ? data.map((column) => column.summary ? "minmax(auto, max-content)" : columnTrack).join(" ") : `repeat(${columns}, ${columnTrack})`}`,
|
|
6289
7066
|
gridTemplateRows: `auto repeat(${rows}, ${rowTrack})`
|
|
6290
7067
|
};
|
|
6291
7068
|
if (compact) {
|
|
6292
7069
|
gridStyle["--a8c-charts-dimension-heatmap-cell-gap"] = `${compactCellGap}px`;
|
|
6293
7070
|
gridStyle["--a8c-charts-dimension-heatmap-cell-size"] = `${compactCellSize}px`;
|
|
6294
7071
|
}
|
|
7072
|
+
const summaryGaps = (columnIndex) => {
|
|
7073
|
+
if (!data[columnIndex]?.summary) return {};
|
|
7074
|
+
return {
|
|
7075
|
+
[heatmap_chart_module_default["heatmap-chart__gap-start"]]: columnIndex > 0 && !data[columnIndex - 1]?.summary,
|
|
7076
|
+
[heatmap_chart_module_default["heatmap-chart__gap-end"]]: columnIndex < columns - 1 && !data[columnIndex + 1]?.summary
|
|
7077
|
+
};
|
|
7078
|
+
};
|
|
6295
7079
|
const activeDescendant = selectedIndex !== void 0 ? `${chartId}-cell-${Math.floor(selectedIndex / rows)}-${selectedIndex % rows}` : void 0;
|
|
6296
7080
|
const heightCapped = !compact && Boolean(maxCellHeight);
|
|
6297
7081
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeatmapContext.Provider, {
|
|
@@ -6329,7 +7113,10 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
|
|
|
6329
7113
|
"aria-hidden": "true",
|
|
6330
7114
|
className: heatmap_chart_module_default["heatmap-chart__row"],
|
|
6331
7115
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {}), data.map((column, columnIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6332
|
-
className: heatmap_chart_module_default["heatmap-chart__col-label"],
|
|
7116
|
+
className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__col-label"], {
|
|
7117
|
+
[heatmap_chart_module_default["heatmap-chart__col-label--summary"]]: column.summary,
|
|
7118
|
+
...summaryGaps(columnIndex)
|
|
7119
|
+
}),
|
|
6333
7120
|
children: column.label
|
|
6334
7121
|
}, `col-${columnIndex}`))]
|
|
6335
7122
|
}), Array.from({ length: rows }).map((_row, rowIndex) => {
|
|
@@ -6354,7 +7141,8 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
|
|
|
6354
7141
|
}, `cell-${columnIndex}-${rowIndex}`);
|
|
6355
7142
|
const value = cell?.value ?? null;
|
|
6356
7143
|
const present = isPresent(value);
|
|
6357
|
-
const
|
|
7144
|
+
const filled = present && !column.summary;
|
|
7145
|
+
const normalized = filled ? getNormalizedValue(value, extent) : 0;
|
|
6358
7146
|
const flatIndex = columnIndex * rows + rowIndex;
|
|
6359
7147
|
const info = buildTooltipData(columnIndex, rowIndex);
|
|
6360
7148
|
const accessibleLabel = `${info.cellLabel || `${info.columnLabel ?? ""} ${info.rowLabel ?? ""}`.trim()}: ${info.value === null ? (0, _wordpress_i18n.__)("No data", "jetpack-charts") : (0, _automattic_number_formatters.formatNumber)(info.value)}`;
|
|
@@ -6367,14 +7155,16 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
|
|
|
6367
7155
|
"data-column": columnIndex,
|
|
6368
7156
|
"data-row": rowIndex,
|
|
6369
7157
|
className: (0, clsx.default)(heatmap_chart_module_default["heatmap-chart__cell"], {
|
|
6370
|
-
[heatmap_chart_module_default["heatmap-chart__cell--filled"]]:
|
|
6371
|
-
[heatmap_chart_module_default["heatmap-chart__cell--strong"]]:
|
|
7158
|
+
[heatmap_chart_module_default["heatmap-chart__cell--filled"]]: filled,
|
|
7159
|
+
[heatmap_chart_module_default["heatmap-chart__cell--strong"]]: filled && cellHasLightText(normalized),
|
|
7160
|
+
[heatmap_chart_module_default["heatmap-chart__cell--summary"]]: column.summary,
|
|
7161
|
+
...summaryGaps(columnIndex),
|
|
6372
7162
|
[heatmap_chart_module_default["heatmap-chart__cell--selected"]]: selectedIndex === flatIndex
|
|
6373
7163
|
}),
|
|
6374
|
-
style:
|
|
7164
|
+
style: filled ? { "--a8c-charts-heatmap-cell-intensity": normalized } : void 0,
|
|
6375
7165
|
onMouseMove: handleCellMouseMove,
|
|
6376
7166
|
onMouseLeave: handleCellMouseLeave,
|
|
6377
|
-
children: drawValues && present && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7167
|
+
children: (drawValues || column.summary) && present && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6378
7168
|
className: heatmap_chart_module_default["heatmap-chart__cell-value"],
|
|
6379
7169
|
children: (0, _automattic_number_formatters.formatNumberCompact)(value)
|
|
6380
7170
|
})
|
|
@@ -6382,11 +7172,11 @@ const HeatmapChartInternal = ({ data, chartId: providedChartId, width = 0, heigh
|
|
|
6382
7172
|
})]
|
|
6383
7173
|
}, `row-${rowIndex}`);
|
|
6384
7174
|
})]
|
|
6385
|
-
}), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
7175
|
+
}), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BoundedTooltip, {
|
|
6386
7176
|
top: tooltipTop,
|
|
6387
7177
|
left: tooltipLeft,
|
|
6388
7178
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6389
|
-
className:
|
|
7179
|
+
className: standaloneScopeClass,
|
|
6390
7180
|
role: "tooltip",
|
|
6391
7181
|
tabIndex: -1,
|
|
6392
7182
|
children: (renderTooltip ?? defaultRenderTooltip)(tooltipData)
|
|
@@ -6410,7 +7200,43 @@ const HeatmapChartResponsiveInner = (props) => /* @__PURE__ */ (0, react_jsx_run
|
|
|
6410
7200
|
HeatmapChartResponsiveInner.displayName = "HeatmapChart";
|
|
6411
7201
|
const HeatmapChartResponsive = attachSubComponents(withResponsive(HeatmapChartResponsiveInner), { Legend: HeatmapLegend });
|
|
6412
7202
|
//#endregion
|
|
6413
|
-
//#region
|
|
7203
|
+
//#region src/charts/heatmap-chart/use-calendar-heatmap-data.ts
|
|
7204
|
+
/**
|
|
7205
|
+
* `buildCalendarHeatmapData` with the locale and zone taken from
|
|
7206
|
+
* `GlobalChartsProvider` where the caller names neither.
|
|
7207
|
+
*
|
|
7208
|
+
* @param series - Points to bucket. Held by reference, so a caller that rebuilds it each render defeats the memo.
|
|
7209
|
+
* @param options - As for `buildCalendarHeatmapData`; `locale` and `timeZone` win over the provider.
|
|
7210
|
+
* @return Columns and row labels for `HeatmapChart`.
|
|
7211
|
+
*/
|
|
7212
|
+
const useCalendarHeatmapData = (series, options = {}) => {
|
|
7213
|
+
const formatting = useChartFormatting();
|
|
7214
|
+
const locale = options.locale ?? formatting.locale;
|
|
7215
|
+
const timeZone = options.timeZone ?? formatting.timeZone;
|
|
7216
|
+
const { weekStartsOn, hideOutOfRangeDays } = options;
|
|
7217
|
+
const gridStart = options.gridSpan?.start;
|
|
7218
|
+
const gridEnd = options.gridSpan?.end;
|
|
7219
|
+
return (0, react.useMemo)(() => buildCalendarHeatmapData(series, {
|
|
7220
|
+
locale,
|
|
7221
|
+
timeZone,
|
|
7222
|
+
weekStartsOn,
|
|
7223
|
+
hideOutOfRangeDays,
|
|
7224
|
+
gridSpan: {
|
|
7225
|
+
start: gridStart,
|
|
7226
|
+
end: gridEnd
|
|
7227
|
+
}
|
|
7228
|
+
}), [
|
|
7229
|
+
series,
|
|
7230
|
+
locale,
|
|
7231
|
+
timeZone,
|
|
7232
|
+
weekStartsOn,
|
|
7233
|
+
hideOutOfRangeDays,
|
|
7234
|
+
gridStart,
|
|
7235
|
+
gridEnd
|
|
7236
|
+
]);
|
|
7237
|
+
};
|
|
7238
|
+
//#endregion
|
|
7239
|
+
//#region ../../../node_modules/.pnpm/is-plain-object@5.1.0/node_modules/is-plain-object/dist/is-plain-object.mjs
|
|
6414
7240
|
/*!
|
|
6415
7241
|
* is-plain-object <https://github.com/jonschlinkert/is-plain-object>
|
|
6416
7242
|
*
|
|
@@ -6773,7 +7599,7 @@ var require_dist = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
6773
7599
|
tslib_1.__exportStar(require_dist$1(), exports);
|
|
6774
7600
|
}));
|
|
6775
7601
|
//#endregion
|
|
6776
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
7602
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/utils/hooks/use-update-effect.mjs
|
|
6777
7603
|
function useUpdateEffect(effect, deps) {
|
|
6778
7604
|
const mountedRef = (0, react.useRef)(false);
|
|
6779
7605
|
(0, react.useEffect)(() => {
|
|
@@ -8411,7 +9237,7 @@ _createEmotion.css;
|
|
|
8411
9237
|
_createEmotion.sheet;
|
|
8412
9238
|
_createEmotion.cache;
|
|
8413
9239
|
//#endregion
|
|
8414
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9240
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/utils/hooks/use-cx.mjs
|
|
8415
9241
|
var isSerializedStyles = (o) => typeof o !== "undefined" && o !== null && ["name", "styles"].every((p) => typeof o[p] !== "undefined");
|
|
8416
9242
|
var useCx = () => {
|
|
8417
9243
|
const cache = __unsafe_useEmotionCache();
|
|
@@ -8518,7 +9344,7 @@ function memize(fn, options) {
|
|
|
8518
9344
|
return memoized;
|
|
8519
9345
|
}
|
|
8520
9346
|
//#endregion
|
|
8521
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9347
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/utils/colors-values.mjs
|
|
8522
9348
|
var white = "#fff";
|
|
8523
9349
|
var GRAY = {
|
|
8524
9350
|
900: "#1e1e1e",
|
|
@@ -8597,7 +9423,7 @@ var COLORS = Object.freeze({
|
|
|
8597
9423
|
ui: UI
|
|
8598
9424
|
});
|
|
8599
9425
|
//#endregion
|
|
8600
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9426
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/utils/config-values.mjs
|
|
8601
9427
|
var CONTROL_HEIGHT = "36px";
|
|
8602
9428
|
var CONTROL_PROPS = {
|
|
8603
9429
|
controlPaddingX: 12,
|
|
@@ -8612,9 +9438,6 @@ var CONTROL_PROPS = {
|
|
|
8612
9438
|
};
|
|
8613
9439
|
var config_values_default = Object.assign({}, CONTROL_PROPS, {
|
|
8614
9440
|
colorDivider: "rgba(0, 0, 0, 0.1)",
|
|
8615
|
-
colorScrollbarThumb: "rgba(0, 0, 0, 0.2)",
|
|
8616
|
-
colorScrollbarThumbHover: "rgba(0, 0, 0, 0.5)",
|
|
8617
|
-
colorScrollbarTrack: "rgba(0, 0, 0, 0.04)",
|
|
8618
9441
|
elevationIntensity: 1,
|
|
8619
9442
|
radiusXSmall: "1px",
|
|
8620
9443
|
radiusSmall: "2px",
|
|
@@ -8625,7 +9448,6 @@ var config_values_default = Object.assign({}, CONTROL_PROPS, {
|
|
|
8625
9448
|
borderWidth: "1px",
|
|
8626
9449
|
borderWidthFocus: "1.5px",
|
|
8627
9450
|
borderWidthTab: "4px",
|
|
8628
|
-
spinnerSize: 16,
|
|
8629
9451
|
fontSize: "13px",
|
|
8630
9452
|
fontSizeH1: "calc(2.44 * 13px)",
|
|
8631
9453
|
fontSizeH2: "calc(1.95 * 13px)",
|
|
@@ -8702,10 +9524,10 @@ var require_es6 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
8702
9524
|
};
|
|
8703
9525
|
}));
|
|
8704
9526
|
//#endregion
|
|
8705
|
-
//#region ../../../node_modules/.pnpm/@wordpress+warning@3.
|
|
9527
|
+
//#region ../../../node_modules/.pnpm/@wordpress+warning@3.54.0/node_modules/@wordpress/warning/build-module/utils.mjs
|
|
8706
9528
|
var logged = /* @__PURE__ */ new Set();
|
|
8707
9529
|
//#endregion
|
|
8708
|
-
//#region ../../../node_modules/.pnpm/@wordpress+warning@3.
|
|
9530
|
+
//#region ../../../node_modules/.pnpm/@wordpress+warning@3.54.0/node_modules/@wordpress/warning/build-module/index.mjs
|
|
8709
9531
|
function isDev() {
|
|
8710
9532
|
return globalThis.SCRIPT_DEBUG === true;
|
|
8711
9533
|
}
|
|
@@ -8719,7 +9541,7 @@ function warning(message) {
|
|
|
8719
9541
|
logged.add(message);
|
|
8720
9542
|
}
|
|
8721
9543
|
//#endregion
|
|
8722
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9544
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/context/context-system-provider.mjs
|
|
8723
9545
|
var import_es6 = /* @__PURE__ */ __toESM(require_es6(), 1);
|
|
8724
9546
|
var ComponentsContext = (0, react.createContext)(
|
|
8725
9547
|
/** @type {Record<string, any>} */
|
|
@@ -8746,19 +9568,19 @@ var BaseContextSystemProvider = ({ children, value }) => {
|
|
|
8746
9568
|
};
|
|
8747
9569
|
(0, react.memo)(BaseContextSystemProvider);
|
|
8748
9570
|
//#endregion
|
|
8749
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9571
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/context/constants.mjs
|
|
8750
9572
|
var COMPONENT_NAMESPACE = "data-wp-component";
|
|
8751
9573
|
var CONNECTED_NAMESPACE = "data-wp-c16t";
|
|
8752
9574
|
var CONNECT_STATIC_NAMESPACE = "__contextSystemKey__";
|
|
8753
9575
|
//#endregion
|
|
8754
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9576
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/context/get-styled-class-name-from-key.mjs
|
|
8755
9577
|
var import_dist = require_dist();
|
|
8756
9578
|
function getStyledClassName(namespace) {
|
|
8757
9579
|
return `components-${(0, import_dist.paramCase)(namespace)}`;
|
|
8758
9580
|
}
|
|
8759
9581
|
var getStyledClassNameFromKey = memize(getStyledClassName);
|
|
8760
9582
|
//#endregion
|
|
8761
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9583
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/context/context-connect.mjs
|
|
8762
9584
|
function contextConnect(Component, namespace) {
|
|
8763
9585
|
return _contextConnect(Component, namespace, { forwardsRef: true });
|
|
8764
9586
|
}
|
|
@@ -8775,7 +9597,7 @@ function _contextConnect(Component, namespace, options) {
|
|
|
8775
9597
|
});
|
|
8776
9598
|
}
|
|
8777
9599
|
//#endregion
|
|
8778
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9600
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/context/utils.mjs
|
|
8779
9601
|
function getNamespace(componentName) {
|
|
8780
9602
|
return { [COMPONENT_NAMESPACE]: componentName };
|
|
8781
9603
|
}
|
|
@@ -8783,7 +9605,7 @@ function getConnectedNamespace() {
|
|
|
8783
9605
|
return { [CONNECTED_NAMESPACE]: true };
|
|
8784
9606
|
}
|
|
8785
9607
|
//#endregion
|
|
8786
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9608
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/context/use-context-system.mjs
|
|
8787
9609
|
function useContextSystem(props, namespace) {
|
|
8788
9610
|
const contextSystemProps = useComponentsContext();
|
|
8789
9611
|
if (typeof namespace === "undefined") globalThis.SCRIPT_DEBUG === true && warning("useContextSystem: Please provide a namespace");
|
|
@@ -8803,7 +9625,7 @@ function useContextSystem(props, namespace) {
|
|
|
8803
9625
|
return finalComponentProps;
|
|
8804
9626
|
}
|
|
8805
9627
|
//#endregion
|
|
8806
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9628
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/visually-hidden/styles.mjs
|
|
8807
9629
|
var visuallyHidden = {
|
|
8808
9630
|
border: 0,
|
|
8809
9631
|
clip: "rect(1px, 1px, 1px, 1px)",
|
|
@@ -8819,7 +9641,7 @@ var visuallyHidden = {
|
|
|
8819
9641
|
wordBreak: "normal"
|
|
8820
9642
|
};
|
|
8821
9643
|
//#endregion
|
|
8822
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9644
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/utils/polymorphic-element.mjs
|
|
8823
9645
|
var customAttributeRegExp = /^(data|aria|x)-/i;
|
|
8824
9646
|
var eventHandlerRegExp = /^on[A-Z]/;
|
|
8825
9647
|
var svgElementNames = new Set(`animate animateMotion animateTransform circle clipPath defs desc ellipse
|
|
@@ -8909,7 +9731,7 @@ function UnforwardedPolymorphicElement({ as, ...props }, ref) {
|
|
|
8909
9731
|
}
|
|
8910
9732
|
var PolymorphicElement = (0, react.forwardRef)(UnforwardedPolymorphicElement);
|
|
8911
9733
|
//#endregion
|
|
8912
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9734
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/view/component.mjs
|
|
8913
9735
|
function UnforwardedView({ css, ...restProps }, ref) {
|
|
8914
9736
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PolymorphicElement, {
|
|
8915
9737
|
ref,
|
|
@@ -8918,7 +9740,7 @@ function UnforwardedView({ css, ...restProps }, ref) {
|
|
|
8918
9740
|
}
|
|
8919
9741
|
var component_default$2 = Object.assign((0, react.forwardRef)(UnforwardedView), { selector: ".components-view" });
|
|
8920
9742
|
//#endregion
|
|
8921
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9743
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/visually-hidden/component.mjs
|
|
8922
9744
|
function UnconnectedVisuallyHidden(props, forwardedRef) {
|
|
8923
9745
|
const { style: styleProp, ...contextProps } = useContextSystem(props, "VisuallyHidden");
|
|
8924
9746
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default$2, {
|
|
@@ -8933,7 +9755,7 @@ function UnconnectedVisuallyHidden(props, forwardedRef) {
|
|
|
8933
9755
|
}
|
|
8934
9756
|
var component_default$1 = contextConnect(UnconnectedVisuallyHidden, "VisuallyHidden");
|
|
8935
9757
|
//#endregion
|
|
8936
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9758
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/utils/use-responsive-value.mjs
|
|
8937
9759
|
var breakpoints = [
|
|
8938
9760
|
"40em",
|
|
8939
9761
|
"52em",
|
|
@@ -8967,7 +9789,7 @@ function useResponsiveValue(values, options = {}) {
|
|
|
8967
9789
|
return array[index >= array.length ? array.length - 1 : index];
|
|
8968
9790
|
}
|
|
8969
9791
|
//#endregion
|
|
8970
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9792
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/grid/utils.mjs
|
|
8971
9793
|
var ALIGNMENTS = {
|
|
8972
9794
|
bottom: {
|
|
8973
9795
|
alignItems: "flex-end",
|
|
@@ -9015,7 +9837,7 @@ function getAlignmentProps(alignment) {
|
|
|
9015
9837
|
return alignment ? ALIGNMENTS[alignment] : {};
|
|
9016
9838
|
}
|
|
9017
9839
|
//#endregion
|
|
9018
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9840
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/grid/hook.mjs
|
|
9019
9841
|
function useGrid(props) {
|
|
9020
9842
|
const { align, alignment, className, columnGap, columns = 2, gap = 3, isInline = false, justify, rowGap, rows, templateColumns, templateRows, ...otherProps } = useContextSystem(props, "Grid");
|
|
9021
9843
|
const column = useResponsiveValue(Array.isArray(columns) ? columns : [columns]);
|
|
@@ -9036,7 +9858,7 @@ function useGrid(props) {
|
|
|
9036
9858
|
justifyContent: justify,
|
|
9037
9859
|
verticalAlign: isInline ? "middle" : void 0,
|
|
9038
9860
|
...alignmentProps
|
|
9039
|
-
}, process.env.NODE_ENV === "production" ? "" : ";label:gridClasses;", process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,
|
|
9861
|
+
}, process.env.NODE_ENV === "production" ? "" : ";label:gridClasses;", process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImhvb2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBNENzQiIsImZpbGUiOiJob29rLnRzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3NzIH0gZnJvbSAnQGVtb3Rpb24vcmVhY3QnO1xuaW1wb3J0IHsgdXNlTWVtbyB9IGZyb20gJ0B3b3JkcHJlc3MvZWxlbWVudCc7XG5pbXBvcnQgdHlwZSB7IFdvcmRQcmVzc0NvbXBvbmVudFByb3BzIH0gZnJvbSAnLi4vY29udGV4dCc7XG5pbXBvcnQgeyB1c2VDb250ZXh0U3lzdGVtIH0gZnJvbSAnLi4vY29udGV4dCc7XG5pbXBvcnQgeyBnZXRBbGlnbm1lbnRQcm9wcyB9IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHsgdXNlUmVzcG9uc2l2ZVZhbHVlIH0gZnJvbSAnLi4vdXRpbHMvdXNlLXJlc3BvbnNpdmUtdmFsdWUnO1xuaW1wb3J0IENPTkZJRyBmcm9tICcuLi91dGlscy9jb25maWctdmFsdWVzJztcbmltcG9ydCB7IHVzZUN4IH0gZnJvbSAnLi4vdXRpbHMvaG9va3MvdXNlLWN4JztcbmltcG9ydCB0eXBlIHsgR3JpZFByb3BzIH0gZnJvbSAnLi90eXBlcyc7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHVzZUdyaWQoXG5cdHByb3BzOiBXb3JkUHJlc3NDb21wb25lbnRQcm9wczwgR3JpZFByb3BzLCAnZGl2JyA+XG4pIHtcblx0Y29uc3Qge1xuXHRcdGFsaWduLFxuXHRcdGFsaWdubWVudCxcblx0XHRjbGFzc05hbWUsXG5cdFx0Y29sdW1uR2FwLFxuXHRcdGNvbHVtbnMgPSAyLFxuXHRcdGdhcCA9IDMsXG5cdFx0aXNJbmxpbmUgPSBmYWxzZSxcblx0XHRqdXN0aWZ5LFxuXHRcdHJvd0dhcCxcblx0XHRyb3dzLFxuXHRcdHRlbXBsYXRlQ29sdW1ucyxcblx0XHR0ZW1wbGF0ZVJvd3MsXG5cdFx0Li4ub3RoZXJQcm9wc1xuXHR9ID0gdXNlQ29udGV4dFN5c3RlbSggcHJvcHMsICdHcmlkJyApO1xuXG5cdGNvbnN0IGNvbHVtbnNBc0FycmF5ID0gQXJyYXkuaXNBcnJheSggY29sdW1ucyApID8gY29sdW1ucyA6IFsgY29sdW1ucyBdO1xuXHRjb25zdCBjb2x1bW4gPSB1c2VSZXNwb25zaXZlVmFsdWUoIGNvbHVtbnNBc0FycmF5ICk7XG5cdGNvbnN0IHJvd3NBc0FycmF5ID0gQXJyYXkuaXNBcnJheSggcm93cyApID8gcm93cyA6IFsgcm93cyBdO1xuXHRjb25zdCByb3cgPSB1c2VSZXNwb25zaXZlVmFsdWUoIHJvd3NBc0FycmF5ICk7XG5cblx0Y29uc3QgZ3JpZFRlbXBsYXRlQ29sdW1ucyA9XG5cdFx0dGVtcGxhdGVDb2x1bW5zIHx8ICggISEgY29sdW1ucyAmJiBgcmVwZWF0KCAkeyBjb2x1bW4gfSwgMWZyIClgICk7XG5cdGNvbnN0IGdyaWRUZW1wbGF0ZVJvd3MgPVxuXHRcdHRlbXBsYXRlUm93cyB8fCAoICEhIHJvd3MgJiYgYHJlcGVhdCggJHsgcm93IH0sIDFmciApYCApO1xuXG5cdGNvbnN0IGN4ID0gdXNlQ3goKTtcblxuXHRjb25zdCBjbGFzc2VzID0gdXNlTWVtbyggKCkgPT4ge1xuXHRcdGNvbnN0IGFsaWdubWVudFByb3BzID0gZ2V0QWxpZ25tZW50UHJvcHMoIGFsaWdubWVudCApO1xuXG5cdFx0Y29uc3QgZ3JpZENsYXNzZXMgPSBjc3MoIHtcblx0XHRcdGFsaWduSXRlbXM6IGFsaWduLFxuXHRcdFx0ZGlzcGxheTogaXNJbmxpbmUgPyAnaW5saW5lLWdyaWQnIDogJ2dyaWQnLFxuXHRcdFx0Z2FwOiBgY2FsYyggJHsgQ09ORklHLmdyaWRCYXNlIH0gKiAkeyBnYXAgfSApYCxcblx0XHRcdGdyaWRUZW1wbGF0ZUNvbHVtbnM6IGdyaWRUZW1wbGF0ZUNvbHVtbnMgfHwgdW5kZWZpbmVkLFxuXHRcdFx0Z3JpZFRlbXBsYXRlUm93czogZ3JpZFRlbXBsYXRlUm93cyB8fCB1bmRlZmluZWQsXG5cdFx0XHRncmlkUm93R2FwOiByb3dHYXAsXG5cdFx0XHRncmlkQ29sdW1uR2FwOiBjb2x1bW5HYXAsXG5cdFx0XHRqdXN0aWZ5Q29udGVudDoganVzdGlmeSxcblx0XHRcdHZlcnRpY2FsQWxpZ246IGlzSW5saW5lID8gJ21pZGRsZScgOiB1bmRlZmluZWQsXG5cdFx0XHQuLi5hbGlnbm1lbnRQcm9wcyxcblx0XHR9ICk7XG5cblx0XHRyZXR1cm4gY3goIGdyaWRDbGFzc2VzLCBjbGFzc05hbWUgKTtcblx0fSwgW1xuXHRcdGFsaWduLFxuXHRcdGFsaWdubWVudCxcblx0XHRjbGFzc05hbWUsXG5cdFx0Y29sdW1uR2FwLFxuXHRcdGN4LFxuXHRcdGdhcCxcblx0XHRncmlkVGVtcGxhdGVDb2x1bW5zLFxuXHRcdGdyaWRUZW1wbGF0ZVJvd3MsXG5cdFx0aXNJbmxpbmUsXG5cdFx0anVzdGlmeSxcblx0XHRyb3dHYXAsXG5cdF0gKTtcblxuXHRyZXR1cm4geyAuLi5vdGhlclByb3BzLCBjbGFzc05hbWU6IGNsYXNzZXMgfTtcbn1cbiJdfQ== */");
|
|
9040
9862
|
return cx(gridClasses, className);
|
|
9041
9863
|
}, [
|
|
9042
9864
|
align,
|
|
@@ -9057,7 +9879,7 @@ function useGrid(props) {
|
|
|
9057
9879
|
};
|
|
9058
9880
|
}
|
|
9059
9881
|
//#endregion
|
|
9060
|
-
//#region ../../../node_modules/.pnpm/@wordpress+components@
|
|
9882
|
+
//#region ../../../node_modules/.pnpm/@wordpress+components@40.0.0_@date-fns+tz@1.4.1_@types+react-dom@18.3.7_@types+react@18_4546240ebdf94d561ebf2fde285113fe/node_modules/@wordpress/components/build-module/grid/component.mjs
|
|
9061
9883
|
function UnconnectedGrid(props, forwardedRef) {
|
|
9062
9884
|
const gridProps = useGrid(props);
|
|
9063
9885
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default$2, {
|
|
@@ -9080,8 +9902,8 @@ const SUBPIXEL_TOLERANCE = .5;
|
|
|
9080
9902
|
* Counts how many leading rows fit inside the content container.
|
|
9081
9903
|
*
|
|
9082
9904
|
* Rows are read from the DOM rather than derived from constants: row height
|
|
9083
|
-
* depends on
|
|
9084
|
-
* as a label, so any restated number would drift.
|
|
9905
|
+
* depends on `--a8c-charts-dimension-leaderboard-row-gap`, label wrapping, and
|
|
9906
|
+
* whatever a caller renders as a label, so any restated number would drift.
|
|
9085
9907
|
*
|
|
9086
9908
|
* Every row is a direct grid child marked with `data-row-index`; interactive rows
|
|
9087
9909
|
* use a button wrapper and non-interactive rows use a div wrapper.
|
|
@@ -9166,14 +9988,13 @@ function useFittedRowCount(enabled, rowCount, data) {
|
|
|
9166
9988
|
* @return Array of legend items for the leaderboard chart
|
|
9167
9989
|
*/
|
|
9168
9990
|
function useLeaderboardLegendItems({ data, primaryColor, secondaryColor, withComparison = false, withOverlayLabel = false, legendLabels }) {
|
|
9169
|
-
const { leaderboardChart: leaderboardChartSettings } = useGlobalChartsTheme();
|
|
9170
9991
|
const { getElementStyles } = useGlobalChartsContext();
|
|
9171
9992
|
return (0, react.useMemo)(() => {
|
|
9172
9993
|
if (!data || data.length === 0) return [];
|
|
9173
9994
|
const items = [];
|
|
9174
9995
|
const { color: resolvedPrimaryColor } = getElementStyles({
|
|
9175
9996
|
index: 0,
|
|
9176
|
-
overrideColor: primaryColor
|
|
9997
|
+
overrideColor: primaryColor
|
|
9177
9998
|
});
|
|
9178
9999
|
items.push({
|
|
9179
10000
|
label: legendLabels?.primary || (0, _wordpress_i18n.__)("Current period", "jetpack-charts"),
|
|
@@ -9182,7 +10003,7 @@ function useLeaderboardLegendItems({ data, primaryColor, secondaryColor, withCom
|
|
|
9182
10003
|
if (withComparison && !withOverlayLabel) {
|
|
9183
10004
|
const { color: resolvedSecondaryColor } = getElementStyles({
|
|
9184
10005
|
index: 1,
|
|
9185
|
-
overrideColor: secondaryColor
|
|
10006
|
+
overrideColor: secondaryColor
|
|
9186
10007
|
});
|
|
9187
10008
|
items.push({
|
|
9188
10009
|
label: legendLabels?.comparison || (0, _wordpress_i18n.__)("Previous period", "jetpack-charts"),
|
|
@@ -9196,7 +10017,6 @@ function useLeaderboardLegendItems({ data, primaryColor, secondaryColor, withCom
|
|
|
9196
10017
|
secondaryColor,
|
|
9197
10018
|
withComparison,
|
|
9198
10019
|
legendLabels,
|
|
9199
|
-
leaderboardChartSettings,
|
|
9200
10020
|
getElementStyles,
|
|
9201
10021
|
withOverlayLabel
|
|
9202
10022
|
]);
|
|
@@ -9210,6 +10030,9 @@ var leaderboard_chart_module_default = {
|
|
|
9210
10030
|
"chevron": "a8ccharts-GovfoW-chevron",
|
|
9211
10031
|
"deltaPlaceholder": "a8ccharts-GovfoW-deltaPlaceholder",
|
|
9212
10032
|
"deltaValue": "a8ccharts-GovfoW-deltaValue",
|
|
10033
|
+
"deltaValue--down": "a8ccharts-GovfoW-deltaValue--down",
|
|
10034
|
+
"deltaValue--neutral": "a8ccharts-GovfoW-deltaValue--neutral",
|
|
10035
|
+
"deltaValue--up": "a8ccharts-GovfoW-deltaValue--up",
|
|
9213
10036
|
"emptyState": "a8ccharts-GovfoW-emptyState",
|
|
9214
10037
|
"fitEmptyState": "a8ccharts-GovfoW-fitEmptyState",
|
|
9215
10038
|
"interactiveRow": "a8ccharts-GovfoW-interactiveRow",
|
|
@@ -9228,6 +10051,14 @@ var leaderboard_chart_module_default = {
|
|
|
9228
10051
|
};
|
|
9229
10052
|
//#endregion
|
|
9230
10053
|
//#region src/charts/leaderboard-chart/leaderboard-chart.tsx
|
|
10054
|
+
const ROW_GAP = "var(--a8c-charts-dimension-leaderboard-row-gap, 12px)";
|
|
10055
|
+
const COLUMN_GAP = "var(--a8c-charts-dimension-leaderboard-column-gap, 4px)";
|
|
10056
|
+
/** Indexed by `Math.sign( delta ) + 1`, so [negative, neutral, positive]. */
|
|
10057
|
+
const DELTA_TREND_CLASS = [
|
|
10058
|
+
"deltaValue--down",
|
|
10059
|
+
"deltaValue--neutral",
|
|
10060
|
+
"deltaValue--up"
|
|
10061
|
+
];
|
|
9231
10062
|
/**
|
|
9232
10063
|
* Default value formatter using formatMetricValue
|
|
9233
10064
|
*
|
|
@@ -9330,15 +10161,15 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
|
|
|
9330
10161
|
...legend.shapeStyles
|
|
9331
10162
|
};
|
|
9332
10163
|
const { legendChildren, nonLegendChildren } = useChartChildren(children, "LeaderboardChart");
|
|
9333
|
-
const { labelSpacing
|
|
10164
|
+
const { labelSpacing } = leaderboardChartSettings;
|
|
9334
10165
|
const { getElementStyles, isSeriesVisible } = useGlobalChartsContext();
|
|
9335
10166
|
const { color: resolvedPrimaryColor } = getElementStyles({
|
|
9336
10167
|
index: 0,
|
|
9337
|
-
overrideColor: primaryColor
|
|
10168
|
+
overrideColor: primaryColor
|
|
9338
10169
|
});
|
|
9339
10170
|
const { color: resolvedSecondaryColor } = getElementStyles({
|
|
9340
10171
|
index: 1,
|
|
9341
|
-
overrideColor: secondaryColor
|
|
10172
|
+
overrideColor: secondaryColor
|
|
9342
10173
|
});
|
|
9343
10174
|
const legendItems = useLeaderboardLegendItems({
|
|
9344
10175
|
data: data || [],
|
|
@@ -9394,7 +10225,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
|
|
|
9394
10225
|
legendPosition,
|
|
9395
10226
|
legendElement: false,
|
|
9396
10227
|
legendChildren,
|
|
9397
|
-
className: (0, clsx.default)(leaderboard_chart_module_default.leaderboardChart, {
|
|
10228
|
+
className: (0, clsx.default)("leaderboard-chart", leaderboard_chart_module_default.leaderboardChart, {
|
|
9398
10229
|
[leaderboard_chart_module_default["leaderboardChart--responsive"]]: !propWidth && !propHeight,
|
|
9399
10230
|
[leaderboard_chart_module_default["leaderboardChart--loading"]]: loading
|
|
9400
10231
|
}, className),
|
|
@@ -9429,7 +10260,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
|
|
|
9429
10260
|
legendPosition,
|
|
9430
10261
|
legendElement,
|
|
9431
10262
|
legendChildren,
|
|
9432
|
-
className: (0, clsx.default)(leaderboard_chart_module_default.leaderboardChart, {
|
|
10263
|
+
className: (0, clsx.default)("leaderboard-chart", leaderboard_chart_module_default.leaderboardChart, {
|
|
9433
10264
|
[leaderboard_chart_module_default["leaderboardChart--responsive"]]: !propWidth && !propHeight,
|
|
9434
10265
|
[leaderboard_chart_module_default["leaderboardChart--loading"]]: loading
|
|
9435
10266
|
}, className),
|
|
@@ -9451,8 +10282,8 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
|
|
|
9451
10282
|
children: getAllHiddenMessage(legendInteractive, "series")
|
|
9452
10283
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(component_default, {
|
|
9453
10284
|
templateColumns: "minmax(0, 1fr) auto",
|
|
9454
|
-
rowGap,
|
|
9455
|
-
columnGap,
|
|
10285
|
+
rowGap: ROW_GAP,
|
|
10286
|
+
columnGap: COLUMN_GAP,
|
|
9456
10287
|
"data-leaderboard-grid": true,
|
|
9457
10288
|
children: data.map((entry, rowIndex) => {
|
|
9458
10289
|
const rowStyle = shouldFitRows && rowIndex >= fittedCount ? { visibility: "hidden" } : void 0;
|
|
@@ -9461,8 +10292,7 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
|
|
|
9461
10292
|
const hasDelta = hasDeltaValue(entry);
|
|
9462
10293
|
const showComparisonValue = showComparisonColumn && hasDelta;
|
|
9463
10294
|
const showComparisonPlaceholder = showComparisonColumn && !hasDelta;
|
|
9464
|
-
const
|
|
9465
|
-
const deltaColor = deltaColors[colorIndex];
|
|
10295
|
+
const deltaTrendClass = leaderboard_chart_module_default[DELTA_TREND_CLASS[showComparisonValue ? Math.sign(entry.delta) + 1 : 1]];
|
|
9466
10296
|
const rowCells = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Stack, {
|
|
9467
10297
|
direction: "column",
|
|
9468
10298
|
gap: labelSpacing,
|
|
@@ -9483,13 +10313,11 @@ const LeaderboardChartInternal = ({ data, chartId: providedChartId, width: propW
|
|
|
9483
10313
|
children: [
|
|
9484
10314
|
isPrimaryVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Text, { children: valueFormatter(entry.currentValue) }),
|
|
9485
10315
|
showComparisonValue && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_wordpress_ui.Text, {
|
|
9486
|
-
className: leaderboard_chart_module_default.deltaValue,
|
|
9487
|
-
style: { color: deltaColor },
|
|
10316
|
+
className: (0, clsx.default)(leaderboard_chart_module_default.deltaValue, deltaTrendClass),
|
|
9488
10317
|
children: deltaFormatter(entry.delta)
|
|
9489
10318
|
}),
|
|
9490
10319
|
showComparisonPlaceholder && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_wordpress_ui.Text, {
|
|
9491
|
-
className: (0, clsx.default)(leaderboard_chart_module_default.deltaValue, leaderboard_chart_module_default.deltaPlaceholder),
|
|
9492
|
-
style: { color: deltaColor },
|
|
10320
|
+
className: (0, clsx.default)(leaderboard_chart_module_default.deltaValue, leaderboard_chart_module_default.deltaPlaceholder, deltaTrendClass),
|
|
9493
10321
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9494
10322
|
"aria-hidden": "true",
|
|
9495
10323
|
children: "—"
|
|
@@ -9573,7 +10401,9 @@ function RadialWipeAnimation({ id, radius, innerRadius = 0, durationMs = 1e3, wi
|
|
|
9573
10401
|
//#endregion
|
|
9574
10402
|
//#region src/charts/pie-chart/pie-chart.module.scss
|
|
9575
10403
|
var pie_chart_module_default = {
|
|
9576
|
-
"pie-
|
|
10404
|
+
"pie-chart__label-plate": "a8ccharts-gnszbG-pie-chart__label-plate",
|
|
10405
|
+
"pie-chart__label-text": "a8ccharts-gnszbG-pie-chart__label-text",
|
|
10406
|
+
"pie-chart__plot": "a8ccharts-gnszbG-pie-chart__plot",
|
|
9577
10407
|
"pie-chart--responsive": "a8ccharts-gnszbG-pie-chart--responsive"
|
|
9578
10408
|
};
|
|
9579
10409
|
//#endregion
|
|
@@ -9628,11 +10458,8 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
|
|
|
9628
10458
|
const providerTheme = useGlobalChartsTheme();
|
|
9629
10459
|
const chartId = useChartId(providedChartId);
|
|
9630
10460
|
const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = (0, _visx_tooltip.useTooltip)();
|
|
9631
|
-
const
|
|
9632
|
-
|
|
9633
|
-
scroll: true,
|
|
9634
|
-
debounce: 0
|
|
9635
|
-
});
|
|
10461
|
+
const standaloneScopeClass = useStandaloneScopeClass();
|
|
10462
|
+
const containerRef = (0, react.useRef)(null);
|
|
9636
10463
|
const onMouseLeave = (0, react.useCallback)(() => {
|
|
9637
10464
|
if (!withTooltips) return;
|
|
9638
10465
|
hideTooltip();
|
|
@@ -9715,19 +10542,7 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
|
|
|
9715
10542
|
width: propWidth || void 0,
|
|
9716
10543
|
height: propHeight || void 0
|
|
9717
10544
|
},
|
|
9718
|
-
trailingContent: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
9719
|
-
withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipInPortal, {
|
|
9720
|
-
top: tooltipTop || 0,
|
|
9721
|
-
left: tooltipLeft || 0,
|
|
9722
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9723
|
-
className: "a8c-charts-scope",
|
|
9724
|
-
role: "tooltip",
|
|
9725
|
-
children: renderTooltip({ tooltipData })
|
|
9726
|
-
})
|
|
9727
|
-
}),
|
|
9728
|
-
htmlChildren,
|
|
9729
|
-
otherChildren
|
|
9730
|
-
] }),
|
|
10545
|
+
trailingContent: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [htmlChildren, otherChildren] }),
|
|
9731
10546
|
children: ({ contentWidth, contentHeight }) => {
|
|
9732
10547
|
const availableSize = Math.min(contentWidth > 0 ? contentWidth : 300, contentHeight > 0 ? contentHeight : 300);
|
|
9733
10548
|
const actualSize = size ? Math.min(size, availableSize) : availableSize;
|
|
@@ -9740,9 +10555,10 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
|
|
|
9740
10555
|
const innerRadius = thickness === 0 ? 0 : outerRadius * (1 - thickness);
|
|
9741
10556
|
const maxCornerRadius = (outerRadius - innerRadius) / 2;
|
|
9742
10557
|
const cornerRadius = cornerScale ? Math.min(cornerScale * outerRadius, maxCornerRadius) : 0;
|
|
9743
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
10558
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Center, {
|
|
9744
10559
|
ref: containerRef,
|
|
9745
|
-
|
|
10560
|
+
className: pie_chart_module_default["pie-chart__plot"],
|
|
10561
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
9746
10562
|
viewBox: `0 0 ${width} ${height}`,
|
|
9747
10563
|
preserveAspectRatio: "xMidYMid meet",
|
|
9748
10564
|
width,
|
|
@@ -9774,11 +10590,12 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
|
|
|
9774
10590
|
const hasSpaceForLabel = arc.endAngle - arc.startAngle >= .25;
|
|
9775
10591
|
const handleMouseMove = (event) => {
|
|
9776
10592
|
if (!withTooltips) return;
|
|
9777
|
-
|
|
10593
|
+
const bounds = containerRef.current?.getBoundingClientRect();
|
|
10594
|
+
if (!bounds) return;
|
|
9778
10595
|
showTooltip({
|
|
9779
10596
|
tooltipData: arc.data,
|
|
9780
|
-
tooltipLeft: event.clientX -
|
|
9781
|
-
tooltipTop: event.clientY -
|
|
10597
|
+
tooltipLeft: event.clientX - bounds.left + tooltipOffsetX,
|
|
10598
|
+
tooltipTop: event.clientY - bounds.top + tooltipOffsetY
|
|
9782
10599
|
});
|
|
9783
10600
|
};
|
|
9784
10601
|
const pathProps = {
|
|
@@ -9801,20 +10618,20 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
|
|
|
9801
10618
|
const backgroundHeight = fontSize + 12;
|
|
9802
10619
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", {
|
|
9803
10620
|
...groupProps,
|
|
9804
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { ...pathProps }), showLabels && hasSpaceForLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [
|
|
10621
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { ...pathProps }), showLabels && hasSpaceForLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
10622
|
+
className: pie_chart_module_default["pie-chart__label-plate"],
|
|
9805
10623
|
x: centroidX - backgroundWidth / 2,
|
|
9806
10624
|
y: centroidY - backgroundHeight / 2,
|
|
9807
10625
|
width: backgroundWidth,
|
|
9808
10626
|
height: backgroundHeight,
|
|
9809
|
-
fill: providerTheme.labelBackgroundColor,
|
|
9810
10627
|
rx: 4,
|
|
9811
10628
|
ry: 4,
|
|
9812
10629
|
pointerEvents: "none"
|
|
9813
10630
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("text", {
|
|
10631
|
+
className: pie_chart_module_default["pie-chart__label-text"],
|
|
9814
10632
|
x: centroidX,
|
|
9815
10633
|
y: centroidY,
|
|
9816
10634
|
dy: ".33em",
|
|
9817
|
-
fill: providerTheme.labelTextColor || "#333",
|
|
9818
10635
|
fontSize,
|
|
9819
10636
|
textAnchor: "middle",
|
|
9820
10637
|
pointerEvents: "none",
|
|
@@ -9825,7 +10642,15 @@ const PieChartInternal = ({ data, chartId: providedChartId, withTooltips = false
|
|
|
9825
10642
|
}
|
|
9826
10643
|
}), !allSegmentsHidden && svgChildren]
|
|
9827
10644
|
})]
|
|
9828
|
-
})
|
|
10645
|
+
}), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BoundedTooltip, {
|
|
10646
|
+
top: tooltipTop || 0,
|
|
10647
|
+
left: tooltipLeft || 0,
|
|
10648
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10649
|
+
className: standaloneScopeClass,
|
|
10650
|
+
role: "tooltip",
|
|
10651
|
+
children: renderTooltip({ tooltipData })
|
|
10652
|
+
})
|
|
10653
|
+
})]
|
|
9829
10654
|
});
|
|
9830
10655
|
}
|
|
9831
10656
|
})
|
|
@@ -9852,6 +10677,7 @@ var pie_semi_circle_chart_module_default = {
|
|
|
9852
10677
|
"label": "a8ccharts-YtTOxW-label",
|
|
9853
10678
|
"note": "a8ccharts-YtTOxW-note",
|
|
9854
10679
|
"pie-semi-circle-chart": "a8ccharts-YtTOxW-pie-semi-circle-chart",
|
|
10680
|
+
"pie-semi-circle-chart__plot": "a8ccharts-YtTOxW-pie-semi-circle-chart__plot",
|
|
9855
10681
|
"pie-semi-circle-chart--responsive": "a8ccharts-YtTOxW-pie-semi-circle-chart--responsive"
|
|
9856
10682
|
};
|
|
9857
10683
|
//#endregion
|
|
@@ -9901,23 +10727,17 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
|
|
|
9901
10727
|
const legendPosition = legend.position ?? "bottom";
|
|
9902
10728
|
const chartId = useChartId(providedChartId);
|
|
9903
10729
|
const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = (0, _visx_tooltip.useTooltip)();
|
|
9904
|
-
const
|
|
9905
|
-
|
|
9906
|
-
scroll: true,
|
|
9907
|
-
debounce: 0
|
|
9908
|
-
});
|
|
10730
|
+
const standaloneScopeClass = useStandaloneScopeClass();
|
|
10731
|
+
const containerRef = (0, react.useRef)(null);
|
|
9909
10732
|
const handleMouseMove = (0, react.useCallback)((event, arc) => {
|
|
9910
|
-
|
|
10733
|
+
const bounds = containerRef.current?.getBoundingClientRect();
|
|
10734
|
+
if (!bounds) return;
|
|
9911
10735
|
showTooltip({
|
|
9912
10736
|
tooltipData: arc.data,
|
|
9913
|
-
tooltipLeft: event.clientX -
|
|
9914
|
-
tooltipTop: event.clientY -
|
|
10737
|
+
tooltipLeft: event.clientX - bounds.left + tooltipOffsetX,
|
|
10738
|
+
tooltipTop: event.clientY - bounds.top + tooltipOffsetY
|
|
9915
10739
|
});
|
|
9916
10740
|
}, [
|
|
9917
|
-
containerBounds.width,
|
|
9918
|
-
containerBounds.height,
|
|
9919
|
-
containerBounds.left,
|
|
9920
|
-
containerBounds.top,
|
|
9921
10741
|
showTooltip,
|
|
9922
10742
|
tooltipOffsetX,
|
|
9923
10743
|
tooltipOffsetY
|
|
@@ -9966,7 +10786,7 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
|
|
|
9966
10786
|
const errorWidth = propHeight ? Math.min(propWidth || propHeight * 2, propHeight * 2) : effectiveWidth;
|
|
9967
10787
|
const errorHeight = errorWidth / 2;
|
|
9968
10788
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9969
|
-
className: pie_semi_circle_chart_module_default["pie-semi-circle-chart"],
|
|
10789
|
+
className: (0, clsx.default)("pie-semi-circle-chart", pie_semi_circle_chart_module_default["pie-semi-circle-chart"], className),
|
|
9970
10790
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
9971
10791
|
width: errorWidth,
|
|
9972
10792
|
height: errorHeight,
|
|
@@ -10013,19 +10833,7 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
|
|
|
10013
10833
|
width: propWidth || void 0,
|
|
10014
10834
|
height: propHeight || void 0
|
|
10015
10835
|
},
|
|
10016
|
-
trailingContent: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
10017
|
-
withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipInPortal, {
|
|
10018
|
-
top: tooltipTop || 0,
|
|
10019
|
-
left: tooltipLeft || 0,
|
|
10020
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10021
|
-
className: "a8c-charts-scope",
|
|
10022
|
-
role: "tooltip",
|
|
10023
|
-
children: renderTooltip({ tooltipData })
|
|
10024
|
-
})
|
|
10025
|
-
}),
|
|
10026
|
-
htmlChildren,
|
|
10027
|
-
otherChildren
|
|
10028
|
-
] }),
|
|
10836
|
+
trailingContent: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [htmlChildren, otherChildren] }),
|
|
10029
10837
|
children: ({ contentWidth, contentHeight }) => {
|
|
10030
10838
|
const availableWidth = contentWidth > 0 ? contentWidth : effectiveWidth;
|
|
10031
10839
|
const availableHeight = contentHeight > 0 ? contentHeight : propHeight || effectiveWidth / 2;
|
|
@@ -10033,9 +10841,10 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
|
|
|
10033
10841
|
const height = width / 2;
|
|
10034
10842
|
const radius = height;
|
|
10035
10843
|
const innerRadius = radius * (1 - thickness);
|
|
10036
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
10844
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Center, {
|
|
10037
10845
|
ref: containerRef,
|
|
10038
|
-
|
|
10846
|
+
className: pie_semi_circle_chart_module_default["pie-semi-circle-chart__plot"],
|
|
10847
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
10039
10848
|
width,
|
|
10040
10849
|
height,
|
|
10041
10850
|
viewBox: `0 0 ${width} ${height}`,
|
|
@@ -10093,7 +10902,15 @@ const PieSemiCircleChartInternal = ({ data, chartId: providedChartId, width: pro
|
|
|
10093
10902
|
!allSegmentsHidden && svgChildren
|
|
10094
10903
|
] })
|
|
10095
10904
|
})]
|
|
10096
|
-
})
|
|
10905
|
+
}), withTooltips && tooltipOpen && tooltipData && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BoundedTooltip, {
|
|
10906
|
+
top: tooltipTop || 0,
|
|
10907
|
+
left: tooltipLeft || 0,
|
|
10908
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10909
|
+
className: standaloneScopeClass,
|
|
10910
|
+
role: "tooltip",
|
|
10911
|
+
children: renderTooltip({ tooltipData })
|
|
10912
|
+
})
|
|
10913
|
+
})]
|
|
10097
10914
|
});
|
|
10098
10915
|
}
|
|
10099
10916
|
})
|
|
@@ -10351,6 +11168,7 @@ exports.buildCalendarHeatmapData = buildCalendarHeatmapData;
|
|
|
10351
11168
|
exports.defaultTheme = defaultTheme;
|
|
10352
11169
|
exports.formatMetricValue = formatMetricValue;
|
|
10353
11170
|
exports.formatPercentage = formatPercentage;
|
|
11171
|
+
exports.getBucketInfo = getBucketInfo;
|
|
10354
11172
|
exports.getColorDistance = getColorDistance;
|
|
10355
11173
|
exports.hexToRgba = hexToRgba;
|
|
10356
11174
|
exports.isValidHexColor = isValidHexColor;
|
|
@@ -10360,10 +11178,11 @@ exports.mixHexColors = mixHexColors;
|
|
|
10360
11178
|
exports.normalizeColorToHex = normalizeColorToHex;
|
|
10361
11179
|
exports.parseAsLocalDate = parseAsLocalDate;
|
|
10362
11180
|
exports.parseHslString = parseHslString;
|
|
10363
|
-
exports.parseRgbString = parseRgbString;
|
|
10364
11181
|
exports.prefersLightText = prefersLightText;
|
|
10365
11182
|
exports.relativeLuminance = relativeLuminance;
|
|
10366
11183
|
exports.resolveCssVariable = resolveCssVariable;
|
|
11184
|
+
exports.useCalendarHeatmapData = useCalendarHeatmapData;
|
|
11185
|
+
exports.useChartFormatting = useChartFormatting;
|
|
10367
11186
|
exports.useChartLegendItems = useChartLegendItems;
|
|
10368
11187
|
exports.useChartRegistration = useChartRegistration;
|
|
10369
11188
|
exports.useChartScopeElement = useChartScopeElement;
|