@juspay/svelte-ui-components 4.27.6 → 4.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -36
- package/dist/Animations/ModalAnimation.svelte +15 -1
- package/dist/Animations/OverlayAnimation.svelte +7 -1
- package/dist/AreaChart/AreaChart.svelte +366 -85
- package/dist/AspectRatio/AspectRatio.svelte +51 -0
- package/dist/AspectRatio/AspectRatio.svelte.d.ts +3 -0
- package/dist/AspectRatio/properties.d.ts +27 -0
- package/dist/AspectRatio/properties.js +1 -0
- package/dist/Banner/Banner.svelte +10 -4
- package/dist/Banner/properties.d.ts +8 -0
- package/dist/BarChart/BarChart.svelte +156 -49
- package/dist/Book/Book.svelte +14 -1
- package/dist/BrandLoader/BrandLoader.svelte +76 -12
- package/dist/Button/Button.svelte +56 -11
- package/dist/Calendar/Calendar.svelte +88 -61
- package/dist/Card/Card.svelte +1 -1
- package/dist/Carousel/Carousel.svelte +6 -3
- package/dist/ChatComposer/ChatComposer.svelte +54 -4
- package/dist/ChatComposer/dictationState.d.ts +15 -0
- package/dist/ChatComposer/dictationState.js +9 -0
- package/dist/ChatComposer/properties.d.ts +28 -1
- package/dist/ChatMessage/ChatMessage.svelte +27 -1
- package/dist/CheckListItem/CheckListItem.svelte +3 -3
- package/dist/Checkbox/Checkbox.svelte +84 -3
- package/dist/Checkbox/properties.d.ts +45 -13
- package/dist/ChipInput/ChipInput.svelte +70 -3
- package/dist/ChipInput/properties.d.ts +16 -0
- package/dist/Choicebox/Choicebox.svelte +156 -1
- package/dist/Choicebox/group.d.ts +63 -0
- package/dist/Choicebox/group.js +127 -0
- package/dist/Choicebox/properties.d.ts +46 -0
- package/dist/ColorPicker/ColorPicker.svelte +169 -20
- package/dist/ColorPicker/properties.d.ts +16 -0
- package/dist/Combobox/Combobox.svelte +87 -25
- package/dist/Combobox/properties.d.ts +23 -0
- package/dist/CommandMenu/CommandMenu.svelte +27 -11
- package/dist/ContextMenu/ContextMenu.svelte +31 -37
- package/dist/DateRangePicker/DateRangePicker.svelte +171 -73
- package/dist/DeltaIndicator/DeltaIndicator.svelte +3 -3
- package/dist/Draggable/Draggable.svelte +12 -7
- package/dist/DualAxisBarChart/DualAxisBarChart.svelte +11 -4
- package/dist/EmptyState/EmptyState.svelte +1 -1
- package/dist/FileDropzoneTrigger/FileDropzoneTrigger.svelte +1 -1
- package/dist/FileInput/FileInput.svelte +50 -1
- package/dist/FileInput/properties.d.ts +7 -0
- package/dist/FunnelChart/FunnelChart.svelte +40 -12
- package/dist/FunnelChart/properties.d.ts +7 -1
- package/dist/Gallery/Gallery.svelte +20 -13
- package/dist/Gallery/properties.d.ts +8 -0
- package/dist/GridItem/GridItem.svelte +31 -1
- package/dist/HITL/HITL.svelte +35 -9
- package/dist/Icon/Icon.svelte +19 -3
- package/dist/Input/Input.svelte +24 -10
- package/dist/Input/properties.d.ts +37 -1
- package/dist/InputButton/InputButton.svelte +86 -11
- package/dist/InputButton/properties.d.ts +10 -1
- package/dist/Label/Label.svelte +54 -0
- package/dist/Label/Label.svelte.d.ts +3 -0
- package/dist/Label/properties.d.ts +26 -0
- package/dist/Label/properties.js +1 -0
- package/dist/LineChart/LineChart.svelte +385 -94
- package/dist/ListItem/ListItem.svelte +121 -15
- package/dist/Loader/Loader.svelte +22 -2
- package/dist/LoadingDots/LoadingDots.svelte +21 -0
- package/dist/MediaUpload/MediaUpload.svelte +13 -3
- package/dist/Menu/Menu.svelte +56 -28
- package/dist/Modal/Modal.svelte +77 -42
- package/dist/Pagination/Pagination.svelte +9 -2
- package/dist/Phone/Phone.svelte +4 -0
- package/dist/PieChart/PieChart.svelte +282 -38
- package/dist/PieChart/properties.d.ts +9 -0
- package/dist/Pill/Pill.svelte +14 -5
- package/dist/Progress/Progress.svelte +20 -0
- package/dist/Radio/Radio.svelte +58 -1
- package/dist/Radio/properties.d.ts +23 -0
- package/dist/RatingGroup/RatingGroup.svelte +283 -0
- package/dist/RatingGroup/RatingGroup.svelte.d.ts +4 -0
- package/dist/RatingGroup/properties.d.ts +73 -0
- package/dist/RatingGroup/properties.js +1 -0
- package/dist/RelativeTime/RelativeTime.svelte +1 -1
- package/dist/SankeyChart/SankeyChart.svelte +272 -50
- package/dist/SankeyChart/properties.d.ts +9 -0
- package/dist/Scroller/Scroller.svelte +37 -2
- package/dist/Select/Select.svelte +166 -46
- package/dist/Select/properties.d.ts +7 -0
- package/dist/Separator/Separator.svelte +48 -0
- package/dist/Separator/Separator.svelte.d.ts +3 -0
- package/dist/Separator/properties.d.ts +22 -0
- package/dist/Separator/properties.js +1 -0
- package/dist/Sheet/Sheet.svelte +60 -21
- package/dist/Shimmer/Shimmer.svelte +15 -0
- package/dist/Slider/Slider.svelte +87 -6
- package/dist/Slider/properties.d.ts +26 -0
- package/dist/Snippet/Snippet.svelte +4 -0
- package/dist/Snippet/copyState.svelte.d.ts +13 -1
- package/dist/Snippet/copyState.svelte.js +27 -1
- package/dist/Snippet/properties.d.ts +7 -0
- package/dist/SoundKit/SoundKit.js +9 -8
- package/dist/SplitInput/SplitInput.svelte +68 -2
- package/dist/SplitInput/properties.d.ts +23 -0
- package/dist/StatCard/StatCard.svelte +6 -6
- package/dist/Status/Status.svelte +2 -2
- package/dist/Stepper/Step.svelte +19 -6
- package/dist/Stepper/Stepper.svelte +108 -35
- package/dist/Table/BuiltinCell.svelte +2 -2
- package/dist/Table/Table.svelte +373 -55
- package/dist/Table/properties.d.ts +134 -3
- package/dist/Tabs/Tabs.svelte +263 -65
- package/dist/Tabs/properties.d.ts +23 -0
- package/dist/TaskList/TaskList.svelte +14 -5
- package/dist/ThemeSwitcher/ThemeSwitcher.svelte +23 -0
- package/dist/ThinkingIndicator/ThinkingIndicator.svelte +13 -5
- package/dist/Toast/Toast.svelte +27 -2
- package/dist/Toggle/Toggle.svelte +79 -7
- package/dist/Toggle/Toggle.svelte.d.ts +1 -1
- package/dist/Toggle/properties.d.ts +30 -7
- package/dist/ToolCallLog/ToolCallLog.svelte +59 -21
- package/dist/ToolCallLog/properties.d.ts +10 -0
- package/dist/Toolbar/Toolbar.svelte +1 -1
- package/dist/Tooltip/Tooltip.svelte +20 -2
- package/dist/Tooltip/tooltip-action.js +39 -4
- package/dist/TypewriterText/TypewriterText.svelte +34 -2
- package/dist/TypewriterText/properties.d.ts +18 -5
- package/dist/_chart/ChartContainer.svelte +12 -0
- package/dist/_chart/ChartTooltip.svelte +18 -4
- package/dist/_chart/Legend.svelte +21 -0
- package/dist/_chart/Legend.svelte.d.ts +15 -0
- package/dist/_chart/geometry.d.ts +54 -1
- package/dist/_chart/geometry.js +122 -14
- package/dist/_chart/interactions.d.ts +5 -1
- package/dist/_chart/interactions.js +9 -5
- package/dist/_chart/scales.js +16 -0
- package/dist/_chart/temporal.d.ts +45 -0
- package/dist/_chart/temporal.js +116 -0
- package/dist/_field/description.d.ts +50 -0
- package/dist/_field/description.js +51 -0
- package/dist/_interaction/dismissal.d.ts +67 -0
- package/dist/_interaction/dismissal.js +137 -0
- package/dist/_interaction/focus.d.ts +73 -0
- package/dist/_interaction/focus.js +143 -0
- package/dist/assets/star.svg +5 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.js +21 -1
- package/dist/reduced-motion.svelte.d.ts +37 -0
- package/dist/reduced-motion.svelte.js +44 -0
- package/dist/styles/theme-dark.css +778 -0
- package/dist/types.d.ts +4 -2
- package/dist/utils.d.ts +62 -1
- package/dist/utils.js +80 -21
- package/dist-codemod/{bin.js → codemod/bin.js} +13 -2
- package/dist-codemod/{cli.js → codemod/cli.js} +48 -3
- package/dist-codemod/codemod/migrate-audit.js +395 -0
- package/dist-codemod/codemod/migration-assets/legacy-display.css +120 -0
- package/dist-codemod/codemod/migration-assets/legacy-palette.css +106 -0
- package/dist-codemod/codemod/migration-assets/wc-display.json +492 -0
- package/dist-codemod/migrate/analyze.js +804 -0
- package/dist-codemod/migrate/chart-tooltip-selector.js +301 -0
- package/dist-wc/index.js +13992 -4323
- package/package.json +13 -6
- package/dist/Modal/focus-trap.d.ts +0 -42
- package/dist/Modal/focus-trap.js +0 -57
- /package/dist-codemod/{legacy-pairs.js → codemod/legacy-pairs.js} +0 -0
- /package/dist-codemod/{transform.js → codemod/transform.js} +0 -0
- /package/dist-codemod/{wc-children.js → codemod/wc-children.js} +0 -0
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
AreaChartDataPoint,
|
|
4
|
+
AreaChartProperties,
|
|
5
|
+
AreaChartTooltipContext
|
|
6
|
+
} from './properties';
|
|
3
7
|
import { onMount } from 'svelte';
|
|
4
8
|
import ChartContainer from '../_chart/ChartContainer.svelte';
|
|
5
9
|
import Axis from '../_chart/Axis.svelte';
|
|
6
10
|
import ChartTooltip from '../_chart/ChartTooltip.svelte';
|
|
7
11
|
import Legend from '../_chart/Legend.svelte';
|
|
8
12
|
import { createLinearScale, niceLinearDomain, computeLinearTicks } from '../_chart/scales';
|
|
9
|
-
import { computeAutoLayout, computeStackedValues } from '../_chart/geometry';
|
|
13
|
+
import { computeAutoLayout, computeStackedValues, joinByX } from '../_chart/geometry';
|
|
10
14
|
import { linePath, areaPath } from '../_chart/paths';
|
|
11
15
|
import { getColor } from '../_chart/colors';
|
|
12
16
|
import { formatNumber, formatPercent, defaultTickFormat } from '../_chart/format';
|
|
@@ -14,7 +18,7 @@
|
|
|
14
18
|
import { resolvePointLabels } from '../_chart/labels';
|
|
15
19
|
import { pointerPositionIn, dismissOnOutsidePointerDown } from '../_chart/interactions';
|
|
16
20
|
import { formatSeriesAggregate } from '../_chart/aggregate';
|
|
17
|
-
import type { LegendItem, Point } from '../_chart/types';
|
|
21
|
+
import type { LegendItem, Point, TooltipAnchor } from '../_chart/types';
|
|
18
22
|
import { DEFAULT_CHART_MAX_HEIGHT } from '../_chart/types';
|
|
19
23
|
|
|
20
24
|
// ── Props ──────────────────────────────────────────────────────
|
|
@@ -64,7 +68,19 @@
|
|
|
64
68
|
let plotEl: HTMLDivElement | null = $state(null);
|
|
65
69
|
let chartWidth = $state(0);
|
|
66
70
|
let chartHeight = $state(0);
|
|
67
|
-
|
|
71
|
+
// `x` is the shared column key from `joinByX` (a real x VALUE, not an array
|
|
72
|
+
// index); `si` is the series that owns the hovered marker within that
|
|
73
|
+
// column. See the note on `joined` below.
|
|
74
|
+
//
|
|
75
|
+
// `hovered` and `focused` are kept as two separate pieces of state rather
|
|
76
|
+
// than one shared by both input sources: a pointer leaving a mark must
|
|
77
|
+
// never erase that mark's focus, and a late/stale pointerleave -- which
|
|
78
|
+
// real browsers fire *after* a focus event when focusing an off-screen
|
|
79
|
+
// mark auto-scrolls the page under an unmoved cursor -- must not be able
|
|
80
|
+
// to clobber the focus highlight either. See `pointerOrFocused` below for
|
|
81
|
+
// the precedence between them.
|
|
82
|
+
let hovered = $state<{ x: number; si: number } | null>(null);
|
|
83
|
+
let focused = $state<{ x: number; si: number } | null>(null);
|
|
68
84
|
let mouseX = $state(0);
|
|
69
85
|
let mouseY = $state(0);
|
|
70
86
|
|
|
@@ -77,19 +93,49 @@
|
|
|
77
93
|
let isStacked = $derived(stacked || stackNormalize);
|
|
78
94
|
let isEmpty = $derived(series.length === 0 || series.every((s) => s.data.length === 0));
|
|
79
95
|
|
|
96
|
+
// ── Cross-series alignment ──────────────────────────────
|
|
97
|
+
|
|
98
|
+
// The ONE alignment contract for tooltip/hit-testing/normalization — see
|
|
99
|
+
// `$lib/_chart/geometry`. For the common case where every series already
|
|
100
|
+
// shares one x sequence this reproduces the old positional pairing.
|
|
101
|
+
let joined = $derived(joinByX(series));
|
|
102
|
+
let joinedByX = $derived(new Map(joined.map((row) => [row.x, row])));
|
|
103
|
+
|
|
104
|
+
// Sum of every series' (clamped-non-negative) value actually present at x —
|
|
105
|
+
// used by both stackNormalize's percent conversion and its tooltip/label
|
|
106
|
+
// total. Built on `joined` rather than a per-index sum, so a shorter/offset
|
|
107
|
+
// series never has its neighbour's total column attributed to it
|
|
108
|
+
// (index-based totals produced a >100% overshoot because two unrelated
|
|
109
|
+
// x's got summed together).
|
|
110
|
+
function columnTotalAtX(x: number): number {
|
|
111
|
+
const row = joinedByX.get(x);
|
|
112
|
+
if (!row) {
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
return row.values.reduce(
|
|
116
|
+
(sum, entry) =>
|
|
117
|
+
sum + (entry && Number.isFinite(entry.point.y) ? Math.max(0, entry.point.y) : 0),
|
|
118
|
+
0
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
80
122
|
let normalizedSeries = $derived.by(() => {
|
|
81
123
|
if (!stackNormalize) {
|
|
82
124
|
return series.map((s) => s.data);
|
|
83
125
|
}
|
|
84
|
-
const totals = series[0]?.data.map((_, i) =>
|
|
85
|
-
series.reduce((sum, s) => sum + Math.max(0, s.data[i]?.y ?? 0), 0)
|
|
86
|
-
);
|
|
87
126
|
return series.map((s) =>
|
|
88
|
-
s.data.map((d
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
127
|
+
s.data.map((d) => {
|
|
128
|
+
const total = columnTotalAtX(d.x);
|
|
129
|
+
return {
|
|
130
|
+
x: d.x,
|
|
131
|
+
// A gap (non-finite y) stays non-finite rather than being coerced
|
|
132
|
+
// to a fabricated 0% — "absent" and "zero" are not the same
|
|
133
|
+
// value, and computeStackedValues (below) already treats a
|
|
134
|
+
// non-finite y as "no segment here", not "an empty segment".
|
|
135
|
+
y: !Number.isFinite(d.y) ? NaN : total > 0 ? (Math.max(0, d.y) / total) * 100 : 0,
|
|
136
|
+
label: d.label
|
|
137
|
+
};
|
|
138
|
+
})
|
|
93
139
|
);
|
|
94
140
|
});
|
|
95
141
|
|
|
@@ -97,7 +143,9 @@
|
|
|
97
143
|
if (xDomain) {
|
|
98
144
|
return xDomain;
|
|
99
145
|
}
|
|
100
|
-
|
|
146
|
+
// Non-finite x/y mark gap points; they must not poison the
|
|
147
|
+
// auto-domain the way a bare Math.min/max over every value would.
|
|
148
|
+
const allX = series.flatMap((s) => s.data.map((d) => d.x)).filter((x) => Number.isFinite(x));
|
|
101
149
|
if (allX.length === 0) {
|
|
102
150
|
return [0, 1];
|
|
103
151
|
}
|
|
@@ -111,13 +159,15 @@
|
|
|
111
159
|
return [0, 100];
|
|
112
160
|
}
|
|
113
161
|
if (isStacked) {
|
|
114
|
-
const topValues = computeStackedValues(normalizedSeries)
|
|
162
|
+
const topValues = computeStackedValues(normalizedSeries)
|
|
163
|
+
.flatMap((s) => s.map((p) => p.y1))
|
|
164
|
+
.filter((y) => Number.isFinite(y));
|
|
115
165
|
if (topValues.length === 0) {
|
|
116
166
|
return [0, 1];
|
|
117
167
|
}
|
|
118
168
|
return niceLinearDomain(0, Math.max(...topValues));
|
|
119
169
|
}
|
|
120
|
-
const allY = series.flatMap((s) => s.data.map((d) => d.y));
|
|
170
|
+
const allY = series.flatMap((s) => s.data.map((d) => d.y)).filter((y) => Number.isFinite(y));
|
|
121
171
|
if (allY.length === 0) {
|
|
122
172
|
return [0, 1];
|
|
123
173
|
}
|
|
@@ -156,7 +206,18 @@
|
|
|
156
206
|
const botD = linePath(bot, curve);
|
|
157
207
|
const areaD =
|
|
158
208
|
topD + ` L ${bot[0].x} ${bot[0].y}` + botD.replace(/^M [^ ]+ [^ ]+/, '') + ' Z';
|
|
159
|
-
|
|
209
|
+
// computeStackedValues compacts OUT any x where this series has no
|
|
210
|
+
// finite sample, so `seg`'s own array index no longer lines up with
|
|
211
|
+
// series[si].data once a series has a gap or different x coverage
|
|
212
|
+
// than its neighbours (see joinByX's doc comment above `joined`). A
|
|
213
|
+
// focusable mark's aria-label and its hover/click payload both need
|
|
214
|
+
// the REAL series[si].data element, not this compacted array's
|
|
215
|
+
// position -- recover it through the same by-x join every other
|
|
216
|
+
// lookup in this file already goes through, rather than inventing a
|
|
217
|
+
// second index scheme that could drift out of sync with it.
|
|
218
|
+
const xs = seg.map((p) => p.x);
|
|
219
|
+
const dataIndices = xs.map((x) => joinedByX.get(x)?.values[si]?.index ?? -1);
|
|
220
|
+
return { color, points: top, xs, dataIndices, areaD, lineD: topD };
|
|
160
221
|
});
|
|
161
222
|
}
|
|
162
223
|
return series.map((s, si) => {
|
|
@@ -165,6 +226,8 @@
|
|
|
165
226
|
return {
|
|
166
227
|
color,
|
|
167
228
|
points,
|
|
229
|
+
xs: s.data.map((d) => d.x),
|
|
230
|
+
dataIndices: s.data.map((_d, pi) => pi),
|
|
168
231
|
areaD: areaPath(points, dims.innerHeight, curve),
|
|
169
232
|
lineD: linePath(points, curve)
|
|
170
233
|
};
|
|
@@ -185,45 +248,88 @@
|
|
|
185
248
|
|
|
186
249
|
// ── Tooltip ────────────────────────────────────────────────────
|
|
187
250
|
|
|
188
|
-
|
|
189
|
-
|
|
251
|
+
// Precedence: keyboard focus wins over pointer hover -- see the comment on
|
|
252
|
+
// `focused`'s declaration. Hovering series A's mark while series B's mark
|
|
253
|
+
// holds focus keeps B active; A regains hover feedback once focus moves
|
|
254
|
+
// off it. Every derivation below (row lookup, hover line, anchor, tooltip)
|
|
255
|
+
// reads this combined value rather than `hovered` directly, so all of them
|
|
256
|
+
// agree on what "active" means.
|
|
257
|
+
let pointerOrFocused = $derived(focused ?? hovered);
|
|
258
|
+
|
|
259
|
+
let hoveredRow = $derived(
|
|
260
|
+
pointerOrFocused === null ? null : (joinedByX.get(pointerOrFocused.x) ?? null)
|
|
190
261
|
);
|
|
191
262
|
|
|
192
|
-
let
|
|
193
|
-
if (
|
|
263
|
+
let hoverLineX = $derived.by<number | null>(() => {
|
|
264
|
+
if (pointerOrFocused === null) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
const x = xScale(pointerOrFocused.x);
|
|
268
|
+
return Number.isFinite(x) ? x : null;
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Anchors the tooltip to the focused/hovered mark's own rendered position
|
|
272
|
+
// (area.points), not a re-derivation of it -- in `stacked` mode that is
|
|
273
|
+
// the TOP of that series' segment (where its dot/focus-ring actually
|
|
274
|
+
// sits), not the raw un-stacked value's own y. This is the same
|
|
275
|
+
// deliberate stacked-mode approximation docs/AreaChart.md already
|
|
276
|
+
// documents for hover tie-breaks: the tooltip's VALUE is unaffected
|
|
277
|
+
// either way, only where the tooltip visually anchors.
|
|
278
|
+
let anchor = $derived.by<TooltipAnchor | null>(() => {
|
|
279
|
+
if (pointerOrFocused === null) {
|
|
194
280
|
return null;
|
|
195
281
|
}
|
|
196
|
-
const
|
|
197
|
-
|
|
282
|
+
const area = areas[pointerOrFocused.si];
|
|
283
|
+
const pi = area?.xs.indexOf(pointerOrFocused.x) ?? -1;
|
|
284
|
+
if (!area || pi === -1) {
|
|
198
285
|
return null;
|
|
199
286
|
}
|
|
287
|
+
const point = area.points[pi];
|
|
288
|
+
return Number.isFinite(point.x) && Number.isFinite(point.y)
|
|
289
|
+
? { x: point.x + dims.margin.left, y: point.y + dims.margin.top, side: 'top' }
|
|
290
|
+
: null;
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
let tooltipContext = $derived.by<AreaChartTooltipContext | null>(() => {
|
|
294
|
+
if (pointerOrFocused === null || hoveredRow === null) {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
const row = hoveredRow;
|
|
200
298
|
return {
|
|
201
|
-
x:
|
|
299
|
+
x: pointerOrFocused.x,
|
|
202
300
|
points: series.map((s, si) => {
|
|
203
|
-
|
|
301
|
+
// Each series contributes ITS OWN sample at this x column — not
|
|
302
|
+
// whatever sits at the hovered/anchor series' array index.
|
|
303
|
+
const entry = row.values[si];
|
|
204
304
|
return {
|
|
205
305
|
name: s.name,
|
|
206
|
-
y:
|
|
306
|
+
y: entry?.point.y ?? 0,
|
|
207
307
|
color: s.color ?? getColor(si),
|
|
208
|
-
label:
|
|
308
|
+
label: entry?.point.label
|
|
209
309
|
};
|
|
210
310
|
})
|
|
211
311
|
};
|
|
212
312
|
});
|
|
213
313
|
|
|
214
314
|
let tooltipData = $derived.by(() => {
|
|
215
|
-
if (tooltipContext === null) {
|
|
315
|
+
if (tooltipContext === null || hoveredRow === null) {
|
|
216
316
|
return null;
|
|
217
317
|
}
|
|
318
|
+
const row = hoveredRow;
|
|
218
319
|
const title = xTickFormat
|
|
219
320
|
? xTickFormat(tooltipContext.x)
|
|
220
321
|
: `x: ${formatNumber(tooltipContext.x)}`;
|
|
322
|
+
// A series absent at this x, or with a gap (non-finite y) here, has
|
|
323
|
+
// nothing to report — showing "0" would read as a measured zero rather
|
|
324
|
+
// than "no data" (absent/zero/invalid are not synonyms).
|
|
325
|
+
const visible = tooltipContext.points
|
|
326
|
+
.map((p, si) => ({ p, entry: row.values[si] }))
|
|
327
|
+
.filter(({ entry }) => entry !== null && Number.isFinite(entry.point.y));
|
|
221
328
|
if (stackNormalize) {
|
|
222
|
-
const
|
|
223
|
-
const columnTotal = series.reduce((sum, s) => sum + Math.max(0, s.data[pi]?.y ?? 0), 0);
|
|
329
|
+
const columnTotal = columnTotalAtX(tooltipContext.x);
|
|
224
330
|
return {
|
|
225
331
|
title,
|
|
226
|
-
items:
|
|
332
|
+
items: visible.map(({ p }) => ({
|
|
227
333
|
label: p.name,
|
|
228
334
|
value: formatPercent(Math.max(0, p.y), columnTotal),
|
|
229
335
|
color: p.color
|
|
@@ -232,7 +338,7 @@
|
|
|
232
338
|
}
|
|
233
339
|
return {
|
|
234
340
|
title,
|
|
235
|
-
items:
|
|
341
|
+
items: visible.map(({ p }) => ({
|
|
236
342
|
label: p.name,
|
|
237
343
|
value: formatNumber(p.y),
|
|
238
344
|
color: p.color
|
|
@@ -243,9 +349,10 @@
|
|
|
243
349
|
// ── Point labels ───────────────────────────────────────────────
|
|
244
350
|
|
|
245
351
|
function pointDisplayValue(si: number, pi: number): string {
|
|
246
|
-
const
|
|
352
|
+
const point = series[si]?.data[pi];
|
|
353
|
+
const y = point?.y ?? 0;
|
|
247
354
|
if (stackNormalize) {
|
|
248
|
-
const columnTotal =
|
|
355
|
+
const columnTotal = point ? columnTotalAtX(point.x) : 0;
|
|
249
356
|
return formatPercent(Math.max(0, y), columnTotal);
|
|
250
357
|
}
|
|
251
358
|
return formatNumber(y);
|
|
@@ -266,6 +373,34 @@
|
|
|
266
373
|
);
|
|
267
374
|
});
|
|
268
375
|
|
|
376
|
+
// ── Keyboard access (family-wide contract, see docs/PieChart.md#keyboard-access) ─
|
|
377
|
+
|
|
378
|
+
// AreaChart/LineChart are continuous series, not discrete categories like
|
|
379
|
+
// Bar/Funnel/Pie/Sankey/DualAxisBar -- there is no fixed set of "marks" to
|
|
380
|
+
// Tab through independent of data shape. The deliberate call here: each
|
|
381
|
+
// rendered (series, point) pair is its own Tab stop, in series-then-point
|
|
382
|
+
// DOM order, mirroring exactly what pointer hover already lands on via
|
|
383
|
+
// findNearest -- so keyboard and mouse users reach the same set of
|
|
384
|
+
// addressable data points, just via a different input.
|
|
385
|
+
function focusPointLabel(point: AreaChartDataPoint, x: number): string {
|
|
386
|
+
if (point.label) {
|
|
387
|
+
return point.label;
|
|
388
|
+
}
|
|
389
|
+
return xTickFormat ? xTickFormat(x) : formatNumber(x);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Matches the family's per-mark "{name}: {value}" convention (Bar/Funnel/
|
|
393
|
+
// Pie), extended with the "{name} — {series}: {value}" em-dash form for
|
|
394
|
+
// multi-series charts -- BarChart's own tooltip already disambiguates
|
|
395
|
+
// multi-series marks this way, but its aria-label omits the series name
|
|
396
|
+
// (a gap found while extracting this contract, not repeated here).
|
|
397
|
+
function focusAriaLabel(si: number, dataIndex: number, x: number): string {
|
|
398
|
+
const point = series[si]?.data[dataIndex];
|
|
399
|
+
const name = point ? focusPointLabel(point, x) : formatNumber(x);
|
|
400
|
+
const value = pointDisplayValue(si, dataIndex);
|
|
401
|
+
return series.length > 1 ? `${name} — ${series[si].name}: ${value}` : `${name}: ${value}`;
|
|
402
|
+
}
|
|
403
|
+
|
|
269
404
|
// ── Interactions ───────────────────────────────────────────────
|
|
270
405
|
|
|
271
406
|
function trackMouse(e: PointerEvent) {
|
|
@@ -276,40 +411,94 @@
|
|
|
276
411
|
}
|
|
277
412
|
}
|
|
278
413
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const reference = series.reduce((a, b) => (b.data.length > a.data.length ? b : a)).data;
|
|
286
|
-
if (reference.length === 0) {
|
|
287
|
-
return null;
|
|
288
|
-
}
|
|
289
|
-
let nearestPi = 0;
|
|
414
|
+
// Nearest hit-test over the SAME `joined` columns used for tooltip/
|
|
415
|
+
// normalization — one alignment contract, not a second position-based
|
|
416
|
+
// path. A column is only eligible if some series actually has a finite
|
|
417
|
+
// value there (a shared gap column is not hoverable).
|
|
418
|
+
function findNearest(plotX: number, plotY: number): { x: number; si: number } | null {
|
|
419
|
+
let nearestRow: (typeof joined)[number] | null = null;
|
|
290
420
|
let nearestXDist = Infinity;
|
|
291
|
-
for (
|
|
292
|
-
const
|
|
421
|
+
for (const row of joined) {
|
|
422
|
+
const hasValue = row.values.some((entry) => entry !== null && Number.isFinite(entry.point.y));
|
|
423
|
+
if (!hasValue) {
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
const px = xScale(row.x);
|
|
293
427
|
const dist = Math.abs(px - plotX);
|
|
294
428
|
if (dist < nearestXDist) {
|
|
295
429
|
nearestXDist = dist;
|
|
296
|
-
|
|
430
|
+
nearestRow = row;
|
|
297
431
|
}
|
|
298
432
|
}
|
|
299
|
-
|
|
433
|
+
if (nearestRow === null) {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
// Nearest by each series' OWN (non-cumulative) value in y-space. In
|
|
437
|
+
// stacked mode this is a deliberate simplification of the previous
|
|
438
|
+
// "nearest by rendered/stacked pixel" tie-break — see docs/AreaChart.md —
|
|
439
|
+
// the value shown for the hovered x is unaffected either way; only which
|
|
440
|
+
// series' marker wins a close proximity call can differ.
|
|
441
|
+
let nearestSi = -1;
|
|
300
442
|
let nearestYDist = Infinity;
|
|
301
|
-
for (let si = 0; si <
|
|
302
|
-
const
|
|
303
|
-
if (!
|
|
443
|
+
for (let si = 0; si < series.length; si++) {
|
|
444
|
+
const entry = nearestRow.values[si];
|
|
445
|
+
if (!entry || !Number.isFinite(entry.point.y)) {
|
|
304
446
|
continue;
|
|
305
447
|
}
|
|
306
|
-
const
|
|
448
|
+
const py = yScale(entry.point.y);
|
|
449
|
+
const dist = Math.abs(py - plotY);
|
|
307
450
|
if (dist < nearestYDist) {
|
|
308
451
|
nearestYDist = dist;
|
|
309
452
|
nearestSi = si;
|
|
310
453
|
}
|
|
311
454
|
}
|
|
312
|
-
return {
|
|
455
|
+
return nearestSi === -1 ? null : { x: nearestRow.x, si: nearestSi };
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function sameMark(a: { x: number; si: number } | null, b: { x: number; si: number } | null) {
|
|
459
|
+
return a !== null && b !== null && a.x === b.x && a.si === b.si;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// onpointhover mirrors the combined pointer-or-focus mark (pointerOrFocused),
|
|
463
|
+
// once per actual change -- e.g. a pointer leaving a mark that is still
|
|
464
|
+
// keyboard-focused must not report a hover-cleared event for a highlight
|
|
465
|
+
// that never went away. Shared by activatePointer/activateFocus/
|
|
466
|
+
// handlePointerLeave/handleBlur so all four write paths agree on the same
|
|
467
|
+
// dedup.
|
|
468
|
+
let lastNotified: { x: number; si: number } | null = null;
|
|
469
|
+
function notifyPointHover() {
|
|
470
|
+
const next = pointerOrFocused;
|
|
471
|
+
if (sameMark(next, lastNotified)) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
lastNotified = next;
|
|
475
|
+
if (next === null) {
|
|
476
|
+
onpointhover?.(null);
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
const entry = joinedByX.get(next.x)?.values[next.si];
|
|
480
|
+
if (entry) {
|
|
481
|
+
// pointIndex is this series' own array index, matching the
|
|
482
|
+
// documented event contract even when series' x coverage differs.
|
|
483
|
+
onpointhover?.({ seriesIndex: next.si, pointIndex: entry.index, point: entry.point });
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Pointer half of activation, used by handleOverlayMove. Skips the write
|
|
488
|
+
// (not just the notify) when the mark hasn't changed, since
|
|
489
|
+
// handleOverlayMove fires on every pointermove over the same mark.
|
|
490
|
+
function activatePointer(next: { x: number; si: number }) {
|
|
491
|
+
if (!sameMark(hovered, next)) {
|
|
492
|
+
hovered = next;
|
|
493
|
+
}
|
|
494
|
+
notifyPointHover();
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Focus half of activation, used by handleMarkFocus. See `focused`'s
|
|
498
|
+
// declaration for why this writes separate state from activatePointer.
|
|
499
|
+
function activateFocus(next: { x: number; si: number }) {
|
|
500
|
+
focused = next;
|
|
501
|
+
notifyPointHover();
|
|
313
502
|
}
|
|
314
503
|
|
|
315
504
|
function handleOverlayMove(e: PointerEvent) {
|
|
@@ -321,27 +510,57 @@
|
|
|
321
510
|
hovered = null;
|
|
322
511
|
return;
|
|
323
512
|
}
|
|
324
|
-
|
|
325
|
-
hovered = next;
|
|
326
|
-
const point = series[next.si].data[next.pi];
|
|
327
|
-
onpointhover?.({ seriesIndex: next.si, pointIndex: next.pi, point });
|
|
328
|
-
}
|
|
513
|
+
activatePointer(next);
|
|
329
514
|
}
|
|
330
515
|
|
|
331
|
-
|
|
516
|
+
// Wired to the hover-overlay's pointerleave. Clears ONLY the hover half of
|
|
517
|
+
// the state -- never `focused` -- so a late, stale pointerleave (see
|
|
518
|
+
// `focused`'s declaration comment) cannot erase a mark's focus highlight.
|
|
519
|
+
function handlePointerLeave() {
|
|
332
520
|
if (hovered !== null) {
|
|
333
521
|
hovered = null;
|
|
334
|
-
|
|
522
|
+
notifyPointHover();
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Keyboard mirror of pointer hover/click: focusing a mark activates it
|
|
527
|
+
// exactly like the nearest-point hover search would (same tooltip/
|
|
528
|
+
// highlight/anchor state, via pointerOrFocused), blur mirrors pointer-leave
|
|
529
|
+
// for the focus half, and Enter/Space invoke the same click callback a
|
|
530
|
+
// pointer click would -- the Enter/Space-only contract every one of the
|
|
531
|
+
// five existing charts uses (none of Bar/DualAxisBar/Funnel/Pie/Sankey
|
|
532
|
+
// wires arrow keys, Home, End or Escape, so none are added here either).
|
|
533
|
+
function handleMarkFocus(x: number, si: number) {
|
|
534
|
+
activateFocus({ x, si });
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Wired to the .focus-target circle's blur. Clears ONLY the focus half; a
|
|
538
|
+
// pointer that happens to still be over the mark keeps it hovered.
|
|
539
|
+
function handleBlur() {
|
|
540
|
+
if (focused !== null) {
|
|
541
|
+
focused = null;
|
|
542
|
+
notifyPointHover();
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function handleMarkKeydown(e: KeyboardEvent) {
|
|
547
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
548
|
+
e.preventDefault();
|
|
549
|
+
handleClick();
|
|
335
550
|
}
|
|
336
551
|
}
|
|
337
552
|
|
|
338
553
|
function handleClick() {
|
|
339
|
-
if (
|
|
554
|
+
if (pointerOrFocused === null) {
|
|
340
555
|
return;
|
|
341
556
|
}
|
|
342
|
-
const
|
|
343
|
-
if (
|
|
344
|
-
onpointclick?.({
|
|
557
|
+
const entry = joinedByX.get(pointerOrFocused.x)?.values[pointerOrFocused.si];
|
|
558
|
+
if (entry) {
|
|
559
|
+
onpointclick?.({
|
|
560
|
+
seriesIndex: pointerOrFocused.si,
|
|
561
|
+
pointIndex: entry.index,
|
|
562
|
+
point: entry.point
|
|
563
|
+
});
|
|
345
564
|
}
|
|
346
565
|
}
|
|
347
566
|
|
|
@@ -351,7 +570,7 @@
|
|
|
351
570
|
if (hovered === null) {
|
|
352
571
|
return;
|
|
353
572
|
}
|
|
354
|
-
return dismissOnOutsidePointerDown(containerEl,
|
|
573
|
+
return dismissOnOutsidePointerDown(containerEl, handlePointerLeave);
|
|
355
574
|
});
|
|
356
575
|
</script>
|
|
357
576
|
|
|
@@ -398,7 +617,7 @@
|
|
|
398
617
|
offset="0%"
|
|
399
618
|
stop-color={area.color}
|
|
400
619
|
stop-opacity={Math.min(
|
|
401
|
-
(
|
|
620
|
+
(pointerOrFocused?.si === si ? fillOpacity + 0.2 : fillOpacity) + 0.3,
|
|
402
621
|
1
|
|
403
622
|
)}
|
|
404
623
|
/>
|
|
@@ -437,25 +656,29 @@
|
|
|
437
656
|
{#each areas as area, si (si)}
|
|
438
657
|
<path
|
|
439
658
|
class="area-fill"
|
|
440
|
-
class:dimmed={
|
|
659
|
+
class:dimmed={pointerOrFocused !== null && pointerOrFocused.si !== si}
|
|
441
660
|
d={area.areaD}
|
|
442
661
|
fill={gradientFill ? `url(#area-grad-${uid}-${si})` : area.color}
|
|
443
|
-
fill-opacity={gradientFill
|
|
662
|
+
fill-opacity={gradientFill
|
|
663
|
+
? 1
|
|
664
|
+
: pointerOrFocused?.si === si
|
|
665
|
+
? fillOpacity + 0.2
|
|
666
|
+
: fillOpacity}
|
|
444
667
|
/>
|
|
445
668
|
{#if showLine}
|
|
446
669
|
<path
|
|
447
670
|
class="area-line"
|
|
448
|
-
class:dimmed={
|
|
671
|
+
class:dimmed={pointerOrFocused !== null && pointerOrFocused.si !== si}
|
|
449
672
|
d={area.lineD}
|
|
450
673
|
stroke={area.color}
|
|
451
674
|
stroke-width={strokeWidth}
|
|
452
675
|
fill="none"
|
|
453
676
|
/>
|
|
454
677
|
{/if}
|
|
455
|
-
{#if area.points.length === 1 && !showDots}
|
|
678
|
+
{#if area.points.length === 1 && !showDots && Number.isFinite(area.points[0].x) && Number.isFinite(area.points[0].y)}
|
|
456
679
|
<circle
|
|
457
680
|
class="single-point"
|
|
458
|
-
class:dimmed={
|
|
681
|
+
class:dimmed={pointerOrFocused !== null && pointerOrFocused.si !== si}
|
|
459
682
|
cx={area.points[0].x}
|
|
460
683
|
cy={area.points[0].y}
|
|
461
684
|
r={6}
|
|
@@ -464,19 +687,27 @@
|
|
|
464
687
|
{/if}
|
|
465
688
|
{#if showDots}
|
|
466
689
|
{#each area.points as point, pi (pi)}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
690
|
+
<!-- Gap points (non-finite) render no marker. -->
|
|
691
|
+
{#if Number.isFinite(point.x) && Number.isFinite(point.y)}
|
|
692
|
+
<circle
|
|
693
|
+
class="dot"
|
|
694
|
+
cx={point.x}
|
|
695
|
+
cy={point.y}
|
|
696
|
+
r={pointerOrFocused !== null &&
|
|
697
|
+
pointerOrFocused.si === si &&
|
|
698
|
+
hoverLineX !== null &&
|
|
699
|
+
point.x === hoverLineX
|
|
700
|
+
? 6
|
|
701
|
+
: 3}
|
|
702
|
+
fill={area.color}
|
|
703
|
+
/>
|
|
704
|
+
{/if}
|
|
474
705
|
{/each}
|
|
475
706
|
{/if}
|
|
476
707
|
{#if showValues && pointLabelPlacements[si]}
|
|
477
708
|
{#each area.points as _point, pi (pi)}
|
|
478
709
|
{@const pl = pointLabelPlacements[si][pi]}
|
|
479
|
-
{#if pl?.visible}
|
|
710
|
+
{#if pl?.visible && Number.isFinite(pl.x) && Number.isFinite(pl.y)}
|
|
480
711
|
<text
|
|
481
712
|
class="point-value"
|
|
482
713
|
x={pl.x}
|
|
@@ -487,6 +718,41 @@
|
|
|
487
718
|
{/if}
|
|
488
719
|
{/each}
|
|
489
720
|
{/if}
|
|
721
|
+
|
|
722
|
+
<!-- Always rendered (independent of showDots): one Tab stop per
|
|
723
|
+
data point, transparent until :focus-visible. Sits under the
|
|
724
|
+
hover-overlay rect below (paint order = DOM order here), so
|
|
725
|
+
pointer hit-testing is untouched -- this layer exists only
|
|
726
|
+
to give keyboard users the same per-point targets pointer
|
|
727
|
+
hover already reaches via findNearest. -->
|
|
728
|
+
{#each area.points as point, pi (pi)}
|
|
729
|
+
{#if Number.isFinite(point.x) && Number.isFinite(point.y)}
|
|
730
|
+
<!-- `onclick` as well as `onkeydown`, matching all five sibling
|
|
731
|
+
charts, which put both on the same element. Assistive
|
|
732
|
+
technology commonly realises its "activate control" gesture
|
|
733
|
+
as a click DISPATCHED AT the focused node rather than as a
|
|
734
|
+
raw keydown, so a mark carrying only `onkeydown` is
|
|
735
|
+
reachable and announceable and still cannot be activated by
|
|
736
|
+
the very users the focus target exists for.
|
|
737
|
+
`pointer-events: none` does not block this: it suppresses
|
|
738
|
+
hit-testing for real pointer input -- which is what keeps
|
|
739
|
+
mouse hover flowing to the overlay underneath -- while a
|
|
740
|
+
dispatched click still fires a listener on the element. -->
|
|
741
|
+
<circle
|
|
742
|
+
class="focus-target"
|
|
743
|
+
cx={point.x}
|
|
744
|
+
cy={point.y}
|
|
745
|
+
r={6}
|
|
746
|
+
tabindex="0"
|
|
747
|
+
role="button"
|
|
748
|
+
aria-label={focusAriaLabel(si, area.dataIndices[pi], area.xs[pi])}
|
|
749
|
+
onfocus={() => handleMarkFocus(area.xs[pi], si)}
|
|
750
|
+
onblur={handleBlur}
|
|
751
|
+
onkeydown={handleMarkKeydown}
|
|
752
|
+
onclick={handleClick}
|
|
753
|
+
/>
|
|
754
|
+
{/if}
|
|
755
|
+
{/each}
|
|
490
756
|
{/each}
|
|
491
757
|
|
|
492
758
|
{#if hoverLineX !== null}
|
|
@@ -503,7 +769,7 @@
|
|
|
503
769
|
height={dims.innerHeight}
|
|
504
770
|
fill="transparent"
|
|
505
771
|
onpointermove={handleOverlayMove}
|
|
506
|
-
onpointerleave={
|
|
772
|
+
onpointerleave={handlePointerLeave}
|
|
507
773
|
onclick={handleClick}
|
|
508
774
|
/>
|
|
509
775
|
</g>
|
|
@@ -514,6 +780,7 @@
|
|
|
514
780
|
data={tooltipData}
|
|
515
781
|
{mouseX}
|
|
516
782
|
{mouseY}
|
|
783
|
+
{anchor}
|
|
517
784
|
portal={tooltipPortal}
|
|
518
785
|
originEl={plotEl}
|
|
519
786
|
unstyled
|
|
@@ -529,6 +796,7 @@
|
|
|
529
796
|
data={tooltipData}
|
|
530
797
|
{mouseX}
|
|
531
798
|
{mouseY}
|
|
799
|
+
{anchor}
|
|
532
800
|
portal={tooltipPortal}
|
|
533
801
|
originEl={plotEl}
|
|
534
802
|
/>
|
|
@@ -547,15 +815,18 @@
|
|
|
547
815
|
}
|
|
548
816
|
.area-fill {
|
|
549
817
|
transition:
|
|
550
|
-
fill-opacity var(--chart-transition-duration, 0.2s)
|
|
551
|
-
|
|
818
|
+
fill-opacity var(--chart-transition-duration, var(--motion-duration, 0.2s))
|
|
819
|
+
var(--chart-transition-easing, var(--motion-easing, ease)),
|
|
820
|
+
opacity var(--chart-transition-duration, var(--motion-duration, 0.2s))
|
|
821
|
+
var(--chart-transition-easing, var(--motion-easing, ease));
|
|
552
822
|
pointer-events: none;
|
|
553
823
|
}
|
|
554
824
|
.area-fill.dimmed {
|
|
555
825
|
opacity: var(--areachart-dimmed-opacity, 0.1);
|
|
556
826
|
}
|
|
557
827
|
.area-line {
|
|
558
|
-
transition: opacity var(--chart-transition-duration, 0.2s)
|
|
828
|
+
transition: opacity var(--chart-transition-duration, var(--motion-duration, 0.2s))
|
|
829
|
+
var(--chart-transition-easing, var(--motion-easing, ease));
|
|
559
830
|
stroke-linecap: round;
|
|
560
831
|
stroke-linejoin: round;
|
|
561
832
|
pointer-events: none;
|
|
@@ -571,12 +842,22 @@
|
|
|
571
842
|
}
|
|
572
843
|
.dot {
|
|
573
844
|
transition:
|
|
574
|
-
r var(--chart-transition-duration, 0.2s)
|
|
575
|
-
|
|
845
|
+
r var(--chart-transition-duration, var(--motion-duration, 0.2s))
|
|
846
|
+
var(--chart-transition-easing, var(--motion-easing, ease)),
|
|
847
|
+
opacity var(--chart-transition-duration, var(--motion-duration, 0.2s))
|
|
848
|
+
var(--chart-transition-easing, var(--motion-easing, ease));
|
|
576
849
|
stroke: var(--chart-dot-stroke, light-dark(#fff, #111827));
|
|
577
850
|
stroke-width: 2;
|
|
578
851
|
pointer-events: none;
|
|
579
852
|
}
|
|
853
|
+
.focus-target {
|
|
854
|
+
fill: transparent;
|
|
855
|
+
pointer-events: none;
|
|
856
|
+
}
|
|
857
|
+
.focus-target:focus-visible {
|
|
858
|
+
outline: 2px solid var(--chart-axis-label-color, light-dark(#333, #e5e7eb));
|
|
859
|
+
outline-offset: 1px;
|
|
860
|
+
}
|
|
580
861
|
.point-value {
|
|
581
862
|
fill: var(--areachart-value-color, light-dark(#333, #e5e7eb));
|
|
582
863
|
font-size: var(--areachart-value-font-size, 11px);
|