@seekalfred/primitives 0.6.0 → 0.7.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 +19 -11
- package/dist/activity-heatmap/activity-heatmap.d.ts +92 -0
- package/dist/activity-heatmap/activity-heatmap.js +699 -0
- package/dist/activity-heatmap/activity-heatmap.js.map +1 -0
- package/dist/activity-heatmap/index.d.ts +2 -0
- package/dist/activity-heatmap/index.js +2 -0
- package/dist/alert/alert.d.ts +5 -1
- package/dist/alert/alert.js +68 -7
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert-dialog/alert-dialog.d.ts +45 -18
- package/dist/alert-dialog/alert-dialog.js +128 -9
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/alert-dialog/index.d.ts +2 -2
- package/dist/alert-dialog/index.js +2 -2
- package/dist/animated-counter/animated-counter.d.ts +38 -0
- package/dist/animated-counter/animated-counter.js +230 -0
- package/dist/animated-counter/animated-counter.js.map +1 -0
- package/dist/animated-counter/index.d.ts +2 -0
- package/dist/animated-counter/index.js +2 -0
- package/dist/announcement-bar/announcement-bar.d.ts +67 -0
- package/dist/announcement-bar/announcement-bar.js +364 -0
- package/dist/announcement-bar/announcement-bar.js.map +1 -0
- package/dist/announcement-bar/index.d.ts +2 -0
- package/dist/announcement-bar/index.js +2 -0
- package/dist/badge/badge.d.ts +1 -1
- package/dist/bar-chart/bar-chart.d.ts +51 -0
- package/dist/bar-chart/bar-chart.js +565 -0
- package/dist/bar-chart/bar-chart.js.map +1 -0
- package/dist/bar-chart/index.d.ts +2 -0
- package/dist/bar-chart/index.js +2 -0
- package/dist/breadcrumb/breadcrumb.d.ts +2 -2
- package/dist/breadcrumb/breadcrumb.js +12 -3
- package/dist/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/brush-chart/brush-chart.d.ts +64 -0
- package/dist/brush-chart/brush-chart.js +1056 -0
- package/dist/brush-chart/brush-chart.js.map +1 -0
- package/dist/brush-chart/index.d.ts +2 -0
- package/dist/brush-chart/index.js +2 -0
- package/dist/button/button.d.ts +2 -2
- package/dist/button/button.js +31 -21
- package/dist/button/button.js.map +1 -1
- package/dist/button-group/button-group.js +3 -4
- package/dist/button-group/button-group.js.map +1 -1
- package/dist/card/card.js +1 -1
- package/dist/card/card.js.map +1 -1
- package/dist/card-stack/card-stack.d.ts +54 -0
- package/dist/card-stack/card-stack.js +492 -0
- package/dist/card-stack/card-stack.js.map +1 -0
- package/dist/card-stack/index.d.ts +2 -0
- package/dist/card-stack/index.js +2 -0
- package/dist/carousel/carousel.d.ts +56 -0
- package/dist/carousel/carousel.js +389 -0
- package/dist/carousel/carousel.js.map +1 -0
- package/dist/carousel/index.d.ts +2 -0
- package/dist/carousel/index.js +2 -0
- package/dist/checkbox/checkbox.js +4 -4
- package/dist/checkbox/checkbox.js.map +1 -1
- package/dist/chip-group/chip-group.d.ts +37 -0
- package/dist/chip-group/chip-group.js +148 -0
- package/dist/chip-group/chip-group.js.map +1 -0
- package/dist/chip-group/index.d.ts +2 -0
- package/dist/chip-group/index.js +2 -0
- package/dist/countdown/countdown.d.ts +45 -0
- package/dist/countdown/countdown.js +298 -0
- package/dist/countdown/countdown.js.map +1 -0
- package/dist/countdown/index.d.ts +2 -0
- package/dist/countdown/index.js +2 -0
- package/dist/date-picker/picker-parts.js +1 -1
- package/dist/date-picker/picker-parts.js.map +1 -1
- package/dist/dialog/dialog.js +1 -1
- package/dist/dialog/dialog.js.map +1 -1
- package/dist/donut-chart/donut-chart.d.ts +64 -0
- package/dist/donut-chart/donut-chart.js +576 -0
- package/dist/donut-chart/donut-chart.js.map +1 -0
- package/dist/donut-chart/index.d.ts +2 -0
- package/dist/donut-chart/index.js +2 -0
- package/dist/empty-state/empty-state.d.ts +21 -10
- package/dist/empty-state/empty-state.js +32 -26
- package/dist/empty-state/empty-state.js.map +1 -1
- package/dist/empty-state/index.d.ts +2 -2
- package/dist/empty-state/index.js +2 -2
- package/dist/file-drop-zone/file-drop-zone.js +1 -1
- package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
- package/dist/filter-bar/filter-bar.d.ts +47 -26
- package/dist/filter-bar/filter-bar.js +272 -156
- package/dist/filter-bar/filter-bar.js.map +1 -1
- package/dist/gauge/gauge.d.ts +45 -0
- package/dist/gauge/gauge.js +219 -0
- package/dist/gauge/gauge.js.map +1 -0
- package/dist/gauge/index.d.ts +2 -0
- package/dist/gauge/index.js +2 -0
- package/dist/in-view-title/in-view-title.d.ts +33 -0
- package/dist/in-view-title/in-view-title.js +119 -0
- package/dist/in-view-title/in-view-title.js.map +1 -0
- package/dist/in-view-title/index.d.ts +2 -0
- package/dist/in-view-title/index.js +2 -0
- package/dist/index.d.ts +49 -6
- package/dist/index.js +48 -8
- package/dist/lib/cn.js +11 -1
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/field-message.js +28 -10
- package/dist/lib/field-message.js.map +1 -1
- package/dist/lib/field-surface.js +9 -8
- package/dist/lib/field-surface.js.map +1 -1
- package/dist/lib/flag-emoji.js +50 -0
- package/dist/lib/flag-emoji.js.map +1 -0
- package/dist/lib/flip.js +11 -3
- package/dist/lib/flip.js.map +1 -1
- package/dist/lib/menu-highlight.js +64 -0
- package/dist/lib/menu-highlight.js.map +1 -0
- package/dist/lib/motion.js +9 -2
- package/dist/lib/motion.js.map +1 -1
- package/dist/lib/rolling-value.js +1 -1
- package/dist/lib/rolling-value.js.map +1 -1
- package/dist/lib/spring-tween.js +500 -0
- package/dist/lib/spring-tween.js.map +1 -0
- package/dist/lib/swap-text.js +3 -1
- package/dist/lib/swap-text.js.map +1 -1
- package/dist/line-chart/index.d.ts +2 -0
- package/dist/line-chart/index.js +2 -0
- package/dist/line-chart/line-chart.d.ts +76 -0
- package/dist/line-chart/line-chart.js +611 -0
- package/dist/line-chart/line-chart.js.map +1 -0
- package/dist/mention-input/index.d.ts +2 -0
- package/dist/mention-input/index.js +2 -0
- package/dist/mention-input/mention-input.d.ts +98 -0
- package/dist/mention-input/mention-input.js +576 -0
- package/dist/mention-input/mention-input.js.map +1 -0
- package/dist/menu/menu.d.ts +2 -0
- package/dist/menu/menu.js +23 -12
- package/dist/menu/menu.js.map +1 -1
- package/dist/money-input/index.d.ts +2 -0
- package/dist/money-input/index.js +2 -0
- package/dist/money-input/money-input.d.ts +65 -0
- package/dist/money-input/money-input.js +607 -0
- package/dist/money-input/money-input.js.map +1 -0
- package/dist/morph-select/index.d.ts +2 -0
- package/dist/morph-select/index.js +2 -0
- package/dist/morph-select/morph-select.d.ts +71 -0
- package/dist/morph-select/morph-select.js +693 -0
- package/dist/morph-select/morph-select.js.map +1 -0
- package/dist/multi-select/index.d.ts +2 -0
- package/dist/multi-select/index.js +2 -0
- package/dist/multi-select/multi-select.d.ts +36 -0
- package/dist/multi-select/multi-select.js +152 -0
- package/dist/multi-select/multi-select.js.map +1 -0
- package/dist/number-field/index.d.ts +2 -0
- package/dist/number-field/index.js +2 -0
- package/dist/number-field/number-field.d.ts +42 -0
- package/dist/number-field/number-field.js +199 -0
- package/dist/number-field/number-field.js.map +1 -0
- package/dist/onboarding-checklist/index.d.ts +2 -0
- package/dist/onboarding-checklist/index.js +2 -0
- package/dist/onboarding-checklist/onboarding-checklist.d.ts +55 -0
- package/dist/onboarding-checklist/onboarding-checklist.js +577 -0
- package/dist/onboarding-checklist/onboarding-checklist.js.map +1 -0
- package/dist/password-field/password-field.js +2 -2
- package/dist/password-field/password-field.js.map +1 -1
- package/dist/phone-input/index.d.ts +3 -0
- package/dist/phone-input/index.js +3 -0
- package/dist/phone-input/phone-countries.d.ts +29 -0
- package/dist/phone-input/phone-countries.js +170 -0
- package/dist/phone-input/phone-countries.js.map +1 -0
- package/dist/phone-input/phone-input.d.ts +66 -0
- package/dist/phone-input/phone-input.js +440 -0
- package/dist/phone-input/phone-input.js.map +1 -0
- package/dist/popover/popover.d.ts +12 -0
- package/dist/popover/popover.js +31 -7
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.js +1 -1
- package/dist/radio-group/radio-group.js +1 -1
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/reactions/index.d.ts +2 -0
- package/dist/reactions/index.js +2 -0
- package/dist/reactions/reactions.d.ts +50 -0
- package/dist/reactions/reactions.js +315 -0
- package/dist/reactions/reactions.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +3 -0
- package/dist/rich-text-editor/index.js +2 -0
- package/dist/rich-text-editor/rich-text-editor.d.ts +70 -0
- package/dist/rich-text-editor/rich-text-editor.js +909 -0
- package/dist/rich-text-editor/rich-text-editor.js.map +1 -0
- package/dist/rich-text-editor/rich-text-engine.d.ts +16 -0
- package/dist/rich-text-editor/rich-text-engine.js +366 -0
- package/dist/rich-text-editor/rich-text-engine.js.map +1 -0
- package/dist/rich-text-editor/rich-text-html.js +174 -0
- package/dist/rich-text-editor/rich-text-html.js.map +1 -0
- package/dist/rich-text-editor/rich-text-icons.js +43 -0
- package/dist/rich-text-editor/rich-text-icons.js.map +1 -0
- package/dist/ridgeline/index.d.ts +2 -0
- package/dist/ridgeline/index.js +2 -0
- package/dist/ridgeline/ridgeline.d.ts +55 -0
- package/dist/ridgeline/ridgeline.js +701 -0
- package/dist/ridgeline/ridgeline.js.map +1 -0
- package/dist/scroll-area/index.d.ts +2 -2
- package/dist/scroll-area/index.js +2 -2
- package/dist/scroll-area/scroll-area.d.ts +68 -31
- package/dist/scroll-area/scroll-area.js +208 -59
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/scrub-input/evaluate.d.ts +14 -0
- package/dist/scrub-input/evaluate.js +72 -0
- package/dist/scrub-input/evaluate.js.map +1 -0
- package/dist/scrub-input/index.d.ts +3 -0
- package/dist/scrub-input/index.js +3 -0
- package/dist/scrub-input/scrub-input.d.ts +74 -0
- package/dist/scrub-input/scrub-input.js +486 -0
- package/dist/scrub-input/scrub-input.js.map +1 -0
- package/dist/search-field/search-field.d.ts +2 -1
- package/dist/search-field/search-field.js +4 -4
- package/dist/search-field/search-field.js.map +1 -1
- package/dist/select/select.d.ts +3 -1
- package/dist/select/select.js +20 -8
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +1 -1
- package/dist/sheet/sheet.js +2 -1
- package/dist/sheet/sheet.js.map +1 -1
- package/dist/shortcut-recorder/index.d.ts +3 -0
- package/dist/shortcut-recorder/index.js +3 -0
- package/dist/shortcut-recorder/shortcut-model.d.ts +27 -0
- package/dist/shortcut-recorder/shortcut-model.js +206 -0
- package/dist/shortcut-recorder/shortcut-model.js.map +1 -0
- package/dist/shortcut-recorder/shortcut-recorder.d.ts +93 -0
- package/dist/shortcut-recorder/shortcut-recorder.js +489 -0
- package/dist/shortcut-recorder/shortcut-recorder.js.map +1 -0
- package/dist/slider/index.d.ts +2 -2
- package/dist/slider/slider.d.ts +46 -14
- package/dist/slider/slider.js +151 -67
- package/dist/slider/slider.js.map +1 -1
- package/dist/slope-chart/index.d.ts +2 -0
- package/dist/slope-chart/index.js +2 -0
- package/dist/slope-chart/slope-chart.d.ts +54 -0
- package/dist/slope-chart/slope-chart.js +568 -0
- package/dist/slope-chart/slope-chart.js.map +1 -0
- package/dist/sparkline/index.d.ts +2 -0
- package/dist/sparkline/index.js +2 -0
- package/dist/sparkline/sparkline.d.ts +46 -0
- package/dist/sparkline/sparkline.js +390 -0
- package/dist/sparkline/sparkline.js.map +1 -0
- package/dist/split-bar/index.d.ts +2 -0
- package/dist/split-bar/index.js +2 -0
- package/dist/split-bar/split-bar.d.ts +62 -0
- package/dist/split-bar/split-bar.js +513 -0
- package/dist/split-bar/split-bar.js.map +1 -0
- package/dist/split-button/split-button.d.ts +1 -1
- package/dist/split-button/split-button.js +12 -11
- package/dist/split-button/split-button.js.map +1 -1
- package/dist/stat-card/index.d.ts +2 -2
- package/dist/stat-card/index.js +2 -2
- package/dist/stat-card/stat-card.d.ts +24 -4
- package/dist/stat-card/stat-card.js +114 -17
- package/dist/stat-card/stat-card.js.map +1 -1
- package/dist/stepper/index.d.ts +2 -0
- package/dist/stepper/index.js +2 -0
- package/dist/stepper/stepper.d.ts +51 -0
- package/dist/stepper/stepper.js +215 -0
- package/dist/stepper/stepper.js.map +1 -0
- package/dist/streamgraph/index.d.ts +2 -0
- package/dist/streamgraph/index.js +2 -0
- package/dist/streamgraph/streamgraph.d.ts +70 -0
- package/dist/streamgraph/streamgraph.js +613 -0
- package/dist/streamgraph/streamgraph.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/swipe-actions/index.d.ts +2 -0
- package/dist/swipe-actions/index.js +2 -0
- package/dist/swipe-actions/swipe-actions.d.ts +67 -0
- package/dist/swipe-actions/swipe-actions.js +552 -0
- package/dist/swipe-actions/swipe-actions.js.map +1 -0
- package/dist/switch/switch.js +2 -2
- package/dist/switch/switch.js.map +1 -1
- package/dist/table/index.d.ts +2 -2
- package/dist/table/index.js +2 -2
- package/dist/table/table.d.ts +56 -4
- package/dist/table/table.js +340 -165
- package/dist/table/table.js.map +1 -1
- package/dist/tabs/tabs.js +2 -2
- package/dist/tabs/tabs.js.map +1 -1
- package/dist/tag-input/index.d.ts +2 -0
- package/dist/tag-input/index.js +2 -0
- package/dist/tag-input/tag-input.d.ts +40 -0
- package/dist/tag-input/tag-input.js +269 -0
- package/dist/tag-input/tag-input.js.map +1 -0
- package/dist/text-morph/index.d.ts +2 -0
- package/dist/text-morph/index.js +2 -0
- package/dist/text-morph/text-morph.d.ts +23 -0
- package/dist/text-morph/text-morph.js +21 -0
- package/dist/text-morph/text-morph.js.map +1 -0
- package/dist/text-reveal/index.d.ts +2 -0
- package/dist/text-reveal/index.js +2 -0
- package/dist/text-reveal/text-reveal.d.ts +27 -0
- package/dist/text-reveal/text-reveal.js +51 -0
- package/dist/text-reveal/text-reveal.js.map +1 -0
- package/dist/text-shimmer/index.d.ts +2 -0
- package/dist/text-shimmer/index.js +2 -0
- package/dist/text-shimmer/text-shimmer.d.ts +29 -0
- package/dist/text-shimmer/text-shimmer.js +29 -0
- package/dist/text-shimmer/text-shimmer.js.map +1 -0
- package/dist/time-picker/time-picker.js +2 -2
- package/dist/time-picker/time-picker.js.map +1 -1
- package/dist/toast/index.d.ts +2 -2
- package/dist/toast/index.js +2 -2
- package/dist/toast/toast.d.ts +6 -0
- package/dist/toast/toast.js +82 -40
- package/dist/toast/toast.js.map +1 -1
- package/dist/tooltip/tooltip.d.ts +9 -2
- package/dist/tooltip/tooltip.js +7 -7
- package/dist/tooltip/tooltip.js.map +1 -1
- package/dist/treemap/index.d.ts +2 -0
- package/dist/treemap/index.js +2 -0
- package/dist/treemap/treemap.d.ts +53 -0
- package/dist/treemap/treemap.js +699 -0
- package/dist/treemap/treemap.js.map +1 -0
- package/dist/usage-meter/index.d.ts +2 -0
- package/dist/usage-meter/index.js +2 -0
- package/dist/usage-meter/usage-meter.d.ts +50 -0
- package/dist/usage-meter/usage-meter.js +440 -0
- package/dist/usage-meter/usage-meter.js.map +1 -0
- package/dist/waffle-chart/index.d.ts +2 -0
- package/dist/waffle-chart/index.js +2 -0
- package/dist/waffle-chart/waffle-chart.d.ts +57 -0
- package/dist/waffle-chart/waffle-chart.js +426 -0
- package/dist/waffle-chart/waffle-chart.js.map +1 -0
- package/package.json +165 -3
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { mergeRefs } from "../lib/merge-refs.js";
|
|
4
|
+
import { prefersReducedMotion, useIsomorphicLayoutEffect } from "../lib/motion.js";
|
|
5
|
+
import { SwapText } from "../lib/swap-text.js";
|
|
6
|
+
import { MORPH, live, retarget } from "../lib/spring-tween.js";
|
|
7
|
+
import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
8
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
//#region src/slope-chart/slope-chart.tsx
|
|
10
|
+
/** Space between labels, around the plot (so dots never touch an edge) and for the column headings, in pixels. */
|
|
11
|
+
const LABEL_GAP = 20;
|
|
12
|
+
const PAD = 14;
|
|
13
|
+
const HEAD = 28;
|
|
14
|
+
const clamp = (value, low, high) => Math.min(high, Math.max(low, value));
|
|
15
|
+
const GROUPED = new Intl.NumberFormat("en-US", { maximumFractionDigits: 1 });
|
|
16
|
+
/** Moves labels apart just enough to stop overlaps: touching labels form a group centred on where its members want to be. */
|
|
17
|
+
function spread(wanted, gap, low, high) {
|
|
18
|
+
let groups = wanted.map((y, index) => ({
|
|
19
|
+
y,
|
|
20
|
+
index
|
|
21
|
+
})).sort((a, b) => a.y - b.y).map((entry) => ({
|
|
22
|
+
members: [entry],
|
|
23
|
+
top: entry.y
|
|
24
|
+
}));
|
|
25
|
+
for (let changed = true; changed;) {
|
|
26
|
+
changed = false;
|
|
27
|
+
const next = [];
|
|
28
|
+
for (const group of groups) {
|
|
29
|
+
const prev = next[next.length - 1];
|
|
30
|
+
if (prev && prev.top + prev.members.length * gap > group.top) {
|
|
31
|
+
const members = [...prev.members, ...group.members];
|
|
32
|
+
next[next.length - 1] = {
|
|
33
|
+
members,
|
|
34
|
+
top: members.reduce((sum, member, k) => sum + member.y - k * gap, 0) / members.length
|
|
35
|
+
};
|
|
36
|
+
changed = true;
|
|
37
|
+
} else next.push(group);
|
|
38
|
+
}
|
|
39
|
+
groups = next.map((group) => ({
|
|
40
|
+
...group,
|
|
41
|
+
top: clamp(group.top, low, Math.max(low, high - (group.members.length - 1) * gap))
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
const out = new Array(wanted.length);
|
|
45
|
+
groups.forEach((group) => group.members.forEach((member, k) => out[member.index] = group.top + k * gap));
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
/** Rank change: an arrow and the number of places, or "=" when level. The number rises or drops in when it changes. */
|
|
49
|
+
function Rank({ move }) {
|
|
50
|
+
const text = move === 0 ? "=" : `${Math.abs(move)}`;
|
|
51
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
52
|
+
"data-move": move > 0 ? "up" : move < 0 ? "down" : "level",
|
|
53
|
+
"aria-hidden": "true",
|
|
54
|
+
className: "slope-chart-rank",
|
|
55
|
+
children: [move !== 0 ? /* @__PURE__ */ jsx("svg", {
|
|
56
|
+
width: "8",
|
|
57
|
+
height: "8",
|
|
58
|
+
viewBox: "0 0 8 8",
|
|
59
|
+
fill: "none",
|
|
60
|
+
stroke: "currentColor",
|
|
61
|
+
strokeWidth: "1.3",
|
|
62
|
+
strokeLinecap: "round",
|
|
63
|
+
strokeLinejoin: "round",
|
|
64
|
+
children: /* @__PURE__ */ jsx("path", { d: move > 0 ? "M4 7V1M1.5 3.5 4 1l2.5 2.5" : "M4 1v6M1.5 4.5 4 7l2.5-2.5" })
|
|
65
|
+
}) : null, /* @__PURE__ */ jsx(SwapText, {
|
|
66
|
+
swapKey: text,
|
|
67
|
+
dir: move >= 0 ? 1 : -1,
|
|
68
|
+
motion: "rise",
|
|
69
|
+
children: text
|
|
70
|
+
})]
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
const SlopeChart = forwardRef(function SlopeChart({ data, label, startLabel, endLabel, formatValue, formatChange, height, highlightKey = null, activeKey, onActiveChange, ranks = true, emptyLabel = "No data", className }, ref) {
|
|
74
|
+
const figure = useRef(null);
|
|
75
|
+
const refs = useMemo(() => mergeRefs(ref, figure), [ref]);
|
|
76
|
+
const stage = useRef(null);
|
|
77
|
+
const plot = useRef(null);
|
|
78
|
+
const tip = useRef(null);
|
|
79
|
+
const empty = data.length === 0;
|
|
80
|
+
const plotHeight = height ?? Math.max(200, data.length * 44);
|
|
81
|
+
const [width, setWidth] = useState(0);
|
|
82
|
+
useIsomorphicLayoutEffect(() => {
|
|
83
|
+
const node = stage.current;
|
|
84
|
+
if (!node) return;
|
|
85
|
+
const read = () => setWidth(node.clientWidth);
|
|
86
|
+
read();
|
|
87
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
88
|
+
const observer = new ResizeObserver(read);
|
|
89
|
+
observer.observe(node);
|
|
90
|
+
return () => observer.disconnect();
|
|
91
|
+
}, []);
|
|
92
|
+
const ready = width > 0;
|
|
93
|
+
const narrow = width < 440;
|
|
94
|
+
const leftRoom = narrow ? width < 300 ? 116 : 132 : 168;
|
|
95
|
+
const rightRoom = ranks ? narrow ? 76 : 96 : narrow ? 52 : 64;
|
|
96
|
+
const x0 = leftRoom;
|
|
97
|
+
const x1 = Math.max(x0 + 40, width - rightRoom);
|
|
98
|
+
const values = data.flatMap((item) => [item.start, item.end]);
|
|
99
|
+
let lo = Math.min(...values);
|
|
100
|
+
let hi = Math.max(...values);
|
|
101
|
+
if (!Number.isFinite(lo)) {
|
|
102
|
+
lo = 0;
|
|
103
|
+
hi = 1;
|
|
104
|
+
}
|
|
105
|
+
if (hi - lo < 1e-9) {
|
|
106
|
+
lo -= 1;
|
|
107
|
+
hi += 1;
|
|
108
|
+
}
|
|
109
|
+
const yOf = (value) => PAD + (1 - (value - lo) / (hi - lo)) * (plotHeight - 28);
|
|
110
|
+
const rankOf = (field) => {
|
|
111
|
+
const sorted = [...data].sort((a, b) => b[field] - a[field]);
|
|
112
|
+
return (item) => sorted.indexOf(item) + 1;
|
|
113
|
+
};
|
|
114
|
+
const r0 = rankOf("start");
|
|
115
|
+
const r1 = rankOf("end");
|
|
116
|
+
const l0 = spread(data.map((item) => yOf(item.start)), LABEL_GAP, 8, plotHeight - 8);
|
|
117
|
+
const l1 = spread(data.map((item) => yOf(item.end)), LABEL_GAP, 8, plotHeight - 8);
|
|
118
|
+
const rows = data.map((item, i) => ({
|
|
119
|
+
item,
|
|
120
|
+
y0: yOf(item.start),
|
|
121
|
+
y1: yOf(item.end),
|
|
122
|
+
l0: l0[i],
|
|
123
|
+
l1: l1[i],
|
|
124
|
+
rank0: r0(item),
|
|
125
|
+
rank1: r1(item),
|
|
126
|
+
order: 0
|
|
127
|
+
}));
|
|
128
|
+
const byEnd = [...rows].sort((a, b) => a.rank1 - b.rank1);
|
|
129
|
+
byEnd.forEach((row, k) => {
|
|
130
|
+
row.order = k;
|
|
131
|
+
});
|
|
132
|
+
const lives = useRef(/* @__PURE__ */ new Map());
|
|
133
|
+
const box = useRef({
|
|
134
|
+
x0,
|
|
135
|
+
x1
|
|
136
|
+
});
|
|
137
|
+
const paint = useCallback(() => {
|
|
138
|
+
const root = plot.current;
|
|
139
|
+
if (!root) return;
|
|
140
|
+
const { x0, x1 } = box.current;
|
|
141
|
+
for (const node of root.querySelectorAll("[data-row]")) {
|
|
142
|
+
const at = lives.current.get(node.dataset.row);
|
|
143
|
+
if (!at) continue;
|
|
144
|
+
const line = node.querySelector("[data-part=line]");
|
|
145
|
+
line?.setAttribute("y1", String(at.y0.value));
|
|
146
|
+
line?.setAttribute("y2", String(at.y1.value));
|
|
147
|
+
node.querySelector("[data-part=dot0]")?.setAttribute("cy", String(at.y0.value));
|
|
148
|
+
node.querySelector("[data-part=dot1]")?.setAttribute("cy", String(at.y1.value));
|
|
149
|
+
node.querySelector("[data-part=lead0]")?.setAttribute("d", `M${x0 - 6},${at.l0.value}L${x0 - 1},${at.y0.value}`);
|
|
150
|
+
node.querySelector("[data-part=lead1]")?.setAttribute("d", `M${x1 + 6},${at.l1.value}L${x1 + 1},${at.y1.value}`);
|
|
151
|
+
}
|
|
152
|
+
for (const node of root.querySelectorAll("[data-label]")) {
|
|
153
|
+
const at = lives.current.get(node.dataset.label);
|
|
154
|
+
if (at) node.style.translate = `0 ${node.dataset.side === "end" ? at.l1.value : at.l0.value}px`;
|
|
155
|
+
}
|
|
156
|
+
}, []);
|
|
157
|
+
useIsomorphicLayoutEffect(() => {
|
|
158
|
+
box.current = {
|
|
159
|
+
x0,
|
|
160
|
+
x1
|
|
161
|
+
};
|
|
162
|
+
paint();
|
|
163
|
+
});
|
|
164
|
+
const signature = rows.map((row) => [
|
|
165
|
+
row.item.key,
|
|
166
|
+
row.y0,
|
|
167
|
+
row.y1,
|
|
168
|
+
row.l0,
|
|
169
|
+
row.l1
|
|
170
|
+
].map((part) => typeof part === "number" ? Math.round(part * 100) : part).join(":")).join("|");
|
|
171
|
+
const latest = useRef(rows);
|
|
172
|
+
useIsomorphicLayoutEffect(() => {
|
|
173
|
+
latest.current = rows;
|
|
174
|
+
});
|
|
175
|
+
useIsomorphicLayoutEffect(() => {
|
|
176
|
+
const current = latest.current;
|
|
177
|
+
const keys = new Set(current.map((row) => row.item.key));
|
|
178
|
+
for (const key of [...lives.current.keys()]) {
|
|
179
|
+
if (keys.has(key)) continue;
|
|
180
|
+
const gone = lives.current.get(key);
|
|
181
|
+
for (const value of Object.values(gone)) value.stop();
|
|
182
|
+
lives.current.delete(key);
|
|
183
|
+
}
|
|
184
|
+
for (const row of current) {
|
|
185
|
+
const at = lives.current.get(row.item.key);
|
|
186
|
+
if (!at) {
|
|
187
|
+
lives.current.set(row.item.key, {
|
|
188
|
+
y0: live(row.y0),
|
|
189
|
+
y1: live(row.y1),
|
|
190
|
+
l0: live(row.l0),
|
|
191
|
+
l1: live(row.l1)
|
|
192
|
+
});
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
retarget(at.y0, row.y0, MORPH, paint);
|
|
196
|
+
retarget(at.y1, row.y1, MORPH, paint);
|
|
197
|
+
retarget(at.l0, row.l0, MORPH, paint);
|
|
198
|
+
retarget(at.l1, row.l1, MORPH, paint);
|
|
199
|
+
}
|
|
200
|
+
paint();
|
|
201
|
+
}, [
|
|
202
|
+
signature,
|
|
203
|
+
paint,
|
|
204
|
+
ready
|
|
205
|
+
]);
|
|
206
|
+
useEffect(() => () => {
|
|
207
|
+
for (const at of lives.current.values()) for (const value of Object.values(at)) value.stop();
|
|
208
|
+
}, []);
|
|
209
|
+
const [drawn, setDrawn] = useState(false);
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
const node = figure.current;
|
|
212
|
+
if (!node || prefersReducedMotion() || typeof IntersectionObserver === "undefined") {
|
|
213
|
+
setDrawn(true);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
217
|
+
if (!entry?.isIntersecting) return;
|
|
218
|
+
observer.disconnect();
|
|
219
|
+
setDrawn(true);
|
|
220
|
+
}, { threshold: .3 });
|
|
221
|
+
observer.observe(node);
|
|
222
|
+
return () => observer.disconnect();
|
|
223
|
+
}, []);
|
|
224
|
+
const [ownActive, setOwnActive] = useState(null);
|
|
225
|
+
const wanted = activeKey !== void 0 ? activeKey : ownActive;
|
|
226
|
+
const active = wanted && data.some((item) => item.key === wanted) ? wanted : null;
|
|
227
|
+
const setActive = (key) => {
|
|
228
|
+
if (activeKey === void 0) setOwnActive(key);
|
|
229
|
+
if (key !== active) onActiveChange?.(key);
|
|
230
|
+
};
|
|
231
|
+
const activeRow = rows.find((row) => row.item.key === active) ?? null;
|
|
232
|
+
const [shownKey, setShownKey] = useState(null);
|
|
233
|
+
if (active && active !== shownKey) setShownKey(active);
|
|
234
|
+
const tipRow = activeRow ?? rows.find((row) => row.item.key === shownKey) ?? null;
|
|
235
|
+
const shownBefore = useRef(false);
|
|
236
|
+
useIsomorphicLayoutEffect(() => {
|
|
237
|
+
const node = tip.current;
|
|
238
|
+
if (!activeRow || !node || !width) {
|
|
239
|
+
shownBefore.current = false;
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const tw = node.offsetWidth;
|
|
243
|
+
const th = node.offsetHeight;
|
|
244
|
+
const mid = (activeRow.y0 + activeRow.y1) / 2;
|
|
245
|
+
const above = mid - th - 16 >= 0;
|
|
246
|
+
const left = clamp((x0 + x1) / 2 - tw / 2, 0, Math.max(0, width - tw));
|
|
247
|
+
const top = clamp(HEAD + (above ? mid - th - 16 : mid + 16), 0, Math.max(0, plotHeight + HEAD - th));
|
|
248
|
+
if (!shownBefore.current) node.style.transition = "none";
|
|
249
|
+
node.style.translate = `${left}px ${top}px`;
|
|
250
|
+
if (!shownBefore.current) {
|
|
251
|
+
node.offsetWidth;
|
|
252
|
+
node.style.transition = "";
|
|
253
|
+
}
|
|
254
|
+
shownBefore.current = true;
|
|
255
|
+
});
|
|
256
|
+
const nearest = (clientX, clientY) => {
|
|
257
|
+
const rect = stage.current?.getBoundingClientRect();
|
|
258
|
+
if (!rect || empty) return null;
|
|
259
|
+
const px = clientX - rect.left;
|
|
260
|
+
const y = clientY - rect.top - HEAD;
|
|
261
|
+
const t = (clamp(px, x0, x1) - x0) / Math.max(1, x1 - x0);
|
|
262
|
+
let best = null;
|
|
263
|
+
let distance = Infinity;
|
|
264
|
+
for (const row of rows) {
|
|
265
|
+
const inLine = Math.abs(row.y0 + (row.y1 - row.y0) * t - y);
|
|
266
|
+
const onLabel = px < x0 ? Math.abs(row.l0 - y) : px > x1 ? Math.abs(row.l1 - y) : Infinity;
|
|
267
|
+
const d = Math.min(inLine, onLabel);
|
|
268
|
+
if (d < distance) {
|
|
269
|
+
distance = d;
|
|
270
|
+
best = row;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return distance <= 22 ? best?.item.key ?? null : null;
|
|
274
|
+
};
|
|
275
|
+
const onKeyDown = (event) => {
|
|
276
|
+
if (empty) return;
|
|
277
|
+
if (event.key === "Escape" && active) {
|
|
278
|
+
event.preventDefault();
|
|
279
|
+
setActive(null);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const at = activeRow ? activeRow.order : -1;
|
|
283
|
+
const moves = {
|
|
284
|
+
ArrowDown: at + 1,
|
|
285
|
+
ArrowRight: at + 1,
|
|
286
|
+
ArrowUp: at < 0 ? 0 : at - 1,
|
|
287
|
+
ArrowLeft: at < 0 ? 0 : at - 1,
|
|
288
|
+
Home: 0,
|
|
289
|
+
End: byEnd.length - 1
|
|
290
|
+
};
|
|
291
|
+
if (!(event.key in moves)) return;
|
|
292
|
+
event.preventDefault();
|
|
293
|
+
setActive(byEnd[clamp(moves[event.key], 0, byEnd.length - 1)].item.key);
|
|
294
|
+
};
|
|
295
|
+
const format = (value) => formatValue ? formatValue(value) : GROUPED.format(value);
|
|
296
|
+
const change = (item) => formatChange ? formatChange(item.end - item.start, item) : `${item.end >= item.start ? "+" : "−"}${format(Math.abs(item.end - item.start))}`;
|
|
297
|
+
const rankText = (row) => row.rank0 === row.rank1 ? "same rank" : row.rank1 < row.rank0 ? `up ${row.rank0 - row.rank1} to rank ${row.rank1}` : `down ${row.rank1 - row.rank0} to rank ${row.rank1}`;
|
|
298
|
+
const describe = (row) => `${row.item.label}, ${startLabel} ${format(row.item.start)}, ${endLabel} ${format(row.item.end)}, ${change(row.item)}, ${rankText(row)}`;
|
|
299
|
+
const summary = empty ? `${label}. ${emptyLabel}.` : `${label}, ${startLabel} to ${endLabel}. ${byEnd.map((row) => `${row.item.label} ${format(row.item.start)} to ${format(row.item.end)}`).join(", ")}.`;
|
|
300
|
+
return /* @__PURE__ */ jsxs("figure", {
|
|
301
|
+
ref: refs,
|
|
302
|
+
"data-slot": "slope-chart",
|
|
303
|
+
"data-drawn": drawn || void 0,
|
|
304
|
+
"data-scrubbing": active !== null || void 0,
|
|
305
|
+
"aria-label": label,
|
|
306
|
+
className: cn("slope-chart box-border font-sans text-foreground", className),
|
|
307
|
+
children: [
|
|
308
|
+
/* @__PURE__ */ jsxs("div", {
|
|
309
|
+
ref: stage,
|
|
310
|
+
"data-slot": "slope-chart-stage",
|
|
311
|
+
"data-narrow": narrow || void 0,
|
|
312
|
+
"data-active": active ? "" : void 0,
|
|
313
|
+
role: empty ? void 0 : "slider",
|
|
314
|
+
tabIndex: empty ? void 0 : 0,
|
|
315
|
+
"aria-label": empty ? void 0 : `${label}, explore items in ${endLabel} order`,
|
|
316
|
+
"aria-valuemin": empty ? void 0 : 1,
|
|
317
|
+
"aria-valuemax": empty ? void 0 : data.length,
|
|
318
|
+
"aria-valuenow": empty ? void 0 : (activeRow ? activeRow.order : 0) + 1,
|
|
319
|
+
"aria-valuetext": empty ? void 0 : activeRow ? describe(activeRow) : summary,
|
|
320
|
+
className: "slope-chart-stage",
|
|
321
|
+
onPointerMove: (event) => {
|
|
322
|
+
if (event.pointerType === "mouse" || event.buttons) setActive(nearest(event.clientX, event.clientY));
|
|
323
|
+
},
|
|
324
|
+
onPointerDown: (event) => {
|
|
325
|
+
if (event.pointerType === "mouse") return;
|
|
326
|
+
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
327
|
+
setActive(nearest(event.clientX, event.clientY));
|
|
328
|
+
},
|
|
329
|
+
onPointerUp: (event) => {
|
|
330
|
+
if (event.pointerType !== "mouse") setActive(null);
|
|
331
|
+
},
|
|
332
|
+
onPointerCancel: () => setActive(null),
|
|
333
|
+
onPointerLeave: (event) => {
|
|
334
|
+
if (event.pointerType === "mouse") setActive(null);
|
|
335
|
+
},
|
|
336
|
+
onKeyDown,
|
|
337
|
+
onBlur: () => setActive(null),
|
|
338
|
+
onFocus: (event) => {
|
|
339
|
+
if (event.currentTarget.matches(":focus-visible") && !empty && !active) setActive(byEnd[0].item.key);
|
|
340
|
+
},
|
|
341
|
+
children: [
|
|
342
|
+
ready && !empty ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
343
|
+
"aria-hidden": "true",
|
|
344
|
+
className: "relative h-7",
|
|
345
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
346
|
+
className: "slope-chart-column",
|
|
347
|
+
style: { left: x0 },
|
|
348
|
+
children: startLabel
|
|
349
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
350
|
+
className: "slope-chart-column",
|
|
351
|
+
style: { left: x1 },
|
|
352
|
+
children: endLabel
|
|
353
|
+
})]
|
|
354
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
355
|
+
ref: plot,
|
|
356
|
+
className: "relative",
|
|
357
|
+
style: { height: plotHeight },
|
|
358
|
+
children: [/* @__PURE__ */ jsxs("svg", {
|
|
359
|
+
width,
|
|
360
|
+
height: plotHeight,
|
|
361
|
+
"aria-hidden": "true",
|
|
362
|
+
focusable: "false",
|
|
363
|
+
className: "block overflow-visible",
|
|
364
|
+
children: [
|
|
365
|
+
/* @__PURE__ */ jsx("line", {
|
|
366
|
+
className: "slope-chart-axis",
|
|
367
|
+
x1: x0,
|
|
368
|
+
x2: x0,
|
|
369
|
+
y1: 0,
|
|
370
|
+
y2: plotHeight
|
|
371
|
+
}),
|
|
372
|
+
/* @__PURE__ */ jsx("line", {
|
|
373
|
+
className: "slope-chart-axis",
|
|
374
|
+
x1,
|
|
375
|
+
x2: x1,
|
|
376
|
+
y1: 0,
|
|
377
|
+
y2: plotHeight
|
|
378
|
+
}),
|
|
379
|
+
rows.map((row, i) => {
|
|
380
|
+
const key = row.item.key;
|
|
381
|
+
return /* @__PURE__ */ jsxs("g", {
|
|
382
|
+
"data-row": key,
|
|
383
|
+
"data-on": active === key || void 0,
|
|
384
|
+
"data-accent": highlightKey === key || void 0,
|
|
385
|
+
"data-dim": active !== null && active !== key || void 0,
|
|
386
|
+
className: "slope-chart-item motion-safe:animate-fade-in",
|
|
387
|
+
style: { "--delay": `${i * 50}ms` },
|
|
388
|
+
children: [
|
|
389
|
+
/* @__PURE__ */ jsx("path", {
|
|
390
|
+
"data-part": "lead0",
|
|
391
|
+
className: "slope-chart-leader",
|
|
392
|
+
d: `M${x0 - 6},${row.l0}L${x0 - 1},${row.y0}`
|
|
393
|
+
}),
|
|
394
|
+
/* @__PURE__ */ jsx("path", {
|
|
395
|
+
"data-part": "lead1",
|
|
396
|
+
className: "slope-chart-leader",
|
|
397
|
+
d: `M${x1 + 6},${row.l1}L${x1 + 1},${row.y1}`
|
|
398
|
+
}),
|
|
399
|
+
/* @__PURE__ */ jsx("line", {
|
|
400
|
+
"data-part": "line",
|
|
401
|
+
className: "slope-chart-line",
|
|
402
|
+
x1: x0,
|
|
403
|
+
x2: x1,
|
|
404
|
+
y1: row.y0,
|
|
405
|
+
y2: row.y1,
|
|
406
|
+
pathLength: 1
|
|
407
|
+
}),
|
|
408
|
+
/* @__PURE__ */ jsx("circle", {
|
|
409
|
+
"data-part": "dot0",
|
|
410
|
+
className: "slope-chart-dot",
|
|
411
|
+
cx: x0,
|
|
412
|
+
cy: row.y0,
|
|
413
|
+
r: 4
|
|
414
|
+
}),
|
|
415
|
+
/* @__PURE__ */ jsx("circle", {
|
|
416
|
+
"data-part": "dot1",
|
|
417
|
+
"data-end": "",
|
|
418
|
+
className: "slope-chart-dot",
|
|
419
|
+
cx: x1,
|
|
420
|
+
cy: row.y1,
|
|
421
|
+
r: 4
|
|
422
|
+
})
|
|
423
|
+
]
|
|
424
|
+
}, key);
|
|
425
|
+
})
|
|
426
|
+
]
|
|
427
|
+
}), rows.map((row, i) => {
|
|
428
|
+
const key = row.item.key;
|
|
429
|
+
const props = {
|
|
430
|
+
"data-label": key,
|
|
431
|
+
"data-on": active === key || void 0,
|
|
432
|
+
"data-accent": highlightKey === key || void 0,
|
|
433
|
+
className: "slope-chart-label"
|
|
434
|
+
};
|
|
435
|
+
const delay = { "--delay": `${i * 50}ms` };
|
|
436
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
437
|
+
"aria-hidden": "true",
|
|
438
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
439
|
+
...props,
|
|
440
|
+
"data-side": "start",
|
|
441
|
+
style: {
|
|
442
|
+
...delay,
|
|
443
|
+
right: width - x0 + 10,
|
|
444
|
+
maxWidth: x0 - 10
|
|
445
|
+
},
|
|
446
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
447
|
+
className: "slope-chart-name",
|
|
448
|
+
children: row.item.label
|
|
449
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
450
|
+
className: "slope-chart-value",
|
|
451
|
+
children: format(row.item.start)
|
|
452
|
+
})]
|
|
453
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
454
|
+
...props,
|
|
455
|
+
"data-side": "end",
|
|
456
|
+
style: {
|
|
457
|
+
...delay,
|
|
458
|
+
left: x1 + 10
|
|
459
|
+
},
|
|
460
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
461
|
+
className: "slope-chart-value",
|
|
462
|
+
children: format(row.item.end)
|
|
463
|
+
}), ranks ? /* @__PURE__ */ jsx(Rank, { move: row.rank0 - row.rank1 }) : null]
|
|
464
|
+
})]
|
|
465
|
+
}, key);
|
|
466
|
+
})]
|
|
467
|
+
})] }) : null,
|
|
468
|
+
/* @__PURE__ */ jsxs("div", {
|
|
469
|
+
ref: tip,
|
|
470
|
+
"aria-hidden": "true",
|
|
471
|
+
"data-slot": "slope-chart-tooltip",
|
|
472
|
+
className: "line-chart-tooltip",
|
|
473
|
+
children: [
|
|
474
|
+
/* @__PURE__ */ jsx("p", {
|
|
475
|
+
className: "m-0 mb-0.5 whitespace-nowrap text-sm font-medium",
|
|
476
|
+
children: tipRow?.item.label ?? ""
|
|
477
|
+
}),
|
|
478
|
+
/* @__PURE__ */ jsxs("p", {
|
|
479
|
+
className: "m-0 flex items-center justify-between gap-3 text-sm",
|
|
480
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
481
|
+
className: "text-text-secondary",
|
|
482
|
+
children: startLabel
|
|
483
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
484
|
+
className: "font-medium tabular-nums",
|
|
485
|
+
children: tipRow ? format(tipRow.item.start) : ""
|
|
486
|
+
})]
|
|
487
|
+
}),
|
|
488
|
+
/* @__PURE__ */ jsxs("p", {
|
|
489
|
+
className: "m-0 flex items-center justify-between gap-3 text-sm",
|
|
490
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
491
|
+
className: "text-text-secondary",
|
|
492
|
+
children: endLabel
|
|
493
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
494
|
+
className: "font-medium tabular-nums",
|
|
495
|
+
children: tipRow ? format(tipRow.item.end) : ""
|
|
496
|
+
})]
|
|
497
|
+
}),
|
|
498
|
+
/* @__PURE__ */ jsx("p", {
|
|
499
|
+
className: "m-0 mt-0.5 text-xs tabular-nums text-muted-foreground",
|
|
500
|
+
children: tipRow ? `${change(tipRow.item)}, ${rankText(tipRow)}` : ""
|
|
501
|
+
})
|
|
502
|
+
]
|
|
503
|
+
}),
|
|
504
|
+
empty ? /* @__PURE__ */ jsx("p", {
|
|
505
|
+
className: "m-0 grid min-h-40 place-items-center text-sm text-muted-foreground",
|
|
506
|
+
children: emptyLabel
|
|
507
|
+
}) : null
|
|
508
|
+
]
|
|
509
|
+
}),
|
|
510
|
+
/* @__PURE__ */ jsx("p", {
|
|
511
|
+
className: "sr-only",
|
|
512
|
+
children: summary
|
|
513
|
+
}),
|
|
514
|
+
/* @__PURE__ */ jsx("p", {
|
|
515
|
+
className: "sr-only",
|
|
516
|
+
"aria-live": "polite",
|
|
517
|
+
"aria-atomic": "true",
|
|
518
|
+
children: activeRow ? describe(activeRow) : ""
|
|
519
|
+
}),
|
|
520
|
+
empty ? null : /* @__PURE__ */ jsx("div", {
|
|
521
|
+
className: "sr-only",
|
|
522
|
+
children: /* @__PURE__ */ jsxs("table", { children: [
|
|
523
|
+
/* @__PURE__ */ jsx("caption", { children: label }),
|
|
524
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
525
|
+
/* @__PURE__ */ jsx("th", {
|
|
526
|
+
scope: "col",
|
|
527
|
+
children: "Item"
|
|
528
|
+
}),
|
|
529
|
+
/* @__PURE__ */ jsx("th", {
|
|
530
|
+
scope: "col",
|
|
531
|
+
children: startLabel
|
|
532
|
+
}),
|
|
533
|
+
/* @__PURE__ */ jsx("th", {
|
|
534
|
+
scope: "col",
|
|
535
|
+
children: endLabel
|
|
536
|
+
}),
|
|
537
|
+
/* @__PURE__ */ jsx("th", {
|
|
538
|
+
scope: "col",
|
|
539
|
+
children: "Change"
|
|
540
|
+
}),
|
|
541
|
+
/* @__PURE__ */ jsx("th", {
|
|
542
|
+
scope: "col",
|
|
543
|
+
children: "Rank"
|
|
544
|
+
})
|
|
545
|
+
] }) }),
|
|
546
|
+
/* @__PURE__ */ jsx("tbody", { children: byEnd.map((row) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
547
|
+
/* @__PURE__ */ jsx("th", {
|
|
548
|
+
scope: "row",
|
|
549
|
+
children: row.item.label
|
|
550
|
+
}),
|
|
551
|
+
/* @__PURE__ */ jsx("td", { children: format(row.item.start) }),
|
|
552
|
+
/* @__PURE__ */ jsx("td", { children: format(row.item.end) }),
|
|
553
|
+
/* @__PURE__ */ jsx("td", { children: change(row.item) }),
|
|
554
|
+
/* @__PURE__ */ jsxs("td", { children: [
|
|
555
|
+
row.rank0,
|
|
556
|
+
" to ",
|
|
557
|
+
row.rank1
|
|
558
|
+
] })
|
|
559
|
+
] }, row.item.key)) })
|
|
560
|
+
] })
|
|
561
|
+
})
|
|
562
|
+
]
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
//#endregion
|
|
566
|
+
export { SlopeChart };
|
|
567
|
+
|
|
568
|
+
//# sourceMappingURL=slope-chart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slope-chart.js","names":[],"sources":["../../src/slope-chart/slope-chart.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type PointerEvent,\n} from \"react\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { mergeRefs } from \"../lib/merge-refs.js\";\nimport { prefersReducedMotion, useIsomorphicLayoutEffect } from \"../lib/motion.js\";\nimport { live, MORPH, retarget, type Live } from \"../lib/spring-tween.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\n\n/**\n * SlopeChart: how several items changed between exactly two moments, and how\n * their order changed. Each item is one line from its first value to its second\n * on a shared axis, with its name and value at each end and the change in rank\n * beside the second value.\n *\n * The lines draw left to right once the chart is in view (a beat apart), the end\n * dots land as each pen arrives, and the labels fade in behind them. New data\n * moves every line, dot, leader and label from where it is on screen. Hovering,\n * touching or the arrow keys bring one item forward, fade the others' lines and\n * show its numbers; the arrow keys walk the items in the second period's order.\n *\n * Plain SVG written straight to the DOM by one small spring tween; labels are\n * real text (they truncate), so they sit beside the SVG and move with it.\n */\nexport interface SlopeItem {\n /** Stable identity. An item keeps its line across datasets, so switching morphs it. */\n key: string;\n /** Name beside the start value, in the tooltip and in the table. */\n label: string;\n /** Value in the first period. */\n start: number;\n /** Value in the second period. */\n end: number;\n}\n\nexport interface SlopeChartProps {\n data: SlopeItem[];\n /** What is measured, such as \"Conversion rate by channel\". Names the chart for assistive technology. */\n label: string;\n /** Heading of the first column, such as \"Q1\". */\n startLabel: string;\n /** Heading of the second column, such as \"Q2\". */\n endLabel: string;\n formatValue?: (value: number) => string;\n /** Formats the change in the tooltip. Defaults to the difference, in formatValue's terms. */\n formatChange?: (change: number, item: SlopeItem) => string;\n /** Plot height in pixels. Defaults to 44 pixels per item, and at least 200. */\n height?: number;\n /** The item drawn in the accent, such as the one the story is about. */\n highlightKey?: string | null;\n /** Controlled item in focus. The others' lines fade while one is in focus. */\n activeKey?: string | null;\n onActiveChange?: (key: string | null) => void;\n /** Rank movement beside each end value. @default true */\n ranks?: boolean;\n /** @default \"No data\" */\n emptyLabel?: string;\n /** Extra classes for the root. Merged last. */\n className?: string;\n}\n\ntype Row = { item: SlopeItem; y0: number; y1: number; l0: number; l1: number; rank0: number; rank1: number; order: number };\ntype RowLive = { y0: Live; y1: Live; l0: Live; l1: Live };\n\n/** Space between labels, around the plot (so dots never touch an edge) and for the column headings, in pixels. */\nconst LABEL_GAP = 20;\nconst PAD = 14;\nconst HEAD = 28;\nconst clamp = (value: number, low: number, high: number) => Math.min(high, Math.max(low, value));\nconst GROUPED = new Intl.NumberFormat(\"en-US\", { maximumFractionDigits: 1 });\n\n/** Moves labels apart just enough to stop overlaps: touching labels form a group centred on where its members want to be. */\nfunction spread(wanted: number[], gap: number, low: number, high: number): number[] {\n const order = wanted.map((y, index) => ({ y, index })).sort((a, b) => a.y - b.y);\n let groups = order.map((entry) => ({ members: [entry], top: entry.y }));\n for (let changed = true; changed; ) {\n changed = false;\n const next: typeof groups = [];\n for (const group of groups) {\n const prev = next[next.length - 1];\n if (prev && prev.top + prev.members.length * gap > group.top) {\n const members = [...prev.members, ...group.members];\n next[next.length - 1] = { members, top: members.reduce((sum, member, k) => sum + member.y - k * gap, 0) / members.length };\n changed = true;\n } else next.push(group);\n }\n groups = next.map((group) => ({ ...group, top: clamp(group.top, low, Math.max(low, high - (group.members.length - 1) * gap)) }));\n }\n const out = new Array<number>(wanted.length);\n groups.forEach((group) => group.members.forEach((member, k) => (out[member.index] = group.top + k * gap)));\n return out;\n}\n\n/** Rank change: an arrow and the number of places, or \"=\" when level. The number rises or drops in when it changes. */\nfunction Rank({ move }: { move: number }) {\n const text = move === 0 ? \"=\" : `${Math.abs(move)}`;\n return (\n <span data-move={move > 0 ? \"up\" : move < 0 ? \"down\" : \"level\"} aria-hidden=\"true\" className=\"slope-chart-rank\">\n {move !== 0 ? (\n <svg width=\"8\" height=\"8\" viewBox=\"0 0 8 8\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.3\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d={move > 0 ? \"M4 7V1M1.5 3.5 4 1l2.5 2.5\" : \"M4 1v6M1.5 4.5 4 7l2.5-2.5\"} />\n </svg>\n ) : null}\n <SwapText swapKey={text} dir={move >= 0 ? 1 : -1} motion=\"rise\">\n {text}\n </SwapText>\n </span>\n );\n}\n\nexport const SlopeChart = forwardRef<HTMLElement, SlopeChartProps>(function SlopeChart(\n {\n data,\n label,\n startLabel,\n endLabel,\n formatValue,\n formatChange,\n height,\n highlightKey = null,\n activeKey,\n onActiveChange,\n ranks = true,\n emptyLabel = \"No data\",\n className,\n },\n ref,\n) {\n const figure = useRef<HTMLElement | null>(null);\n const refs = useMemo(() => mergeRefs(ref, figure), [ref]);\n const stage = useRef<HTMLDivElement>(null);\n const plot = useRef<HTMLDivElement>(null);\n const tip = useRef<HTMLDivElement>(null);\n const empty = data.length === 0;\n const plotHeight = height ?? Math.max(200, data.length * 44);\n\n const [width, setWidth] = useState(0);\n useIsomorphicLayoutEffect(() => {\n const node = stage.current;\n if (!node) return;\n const read = () => setWidth(node.clientWidth);\n read();\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(read);\n observer.observe(node);\n return () => observer.disconnect();\n }, []);\n const ready = width > 0;\n const narrow = width < 440;\n const leftRoom = narrow ? (width < 300 ? 116 : 132) : 168;\n const rightRoom = ranks ? (narrow ? 76 : 96) : narrow ? 52 : 64;\n const x0 = leftRoom;\n const x1 = Math.max(x0 + 40, width - rightRoom);\n\n // One shared value axis for both columns.\n const values = data.flatMap((item) => [item.start, item.end]);\n let lo = Math.min(...values);\n let hi = Math.max(...values);\n if (!Number.isFinite(lo)) {\n lo = 0;\n hi = 1;\n }\n if (hi - lo < 1e-9) {\n lo -= 1;\n hi += 1;\n }\n const yOf = (value: number) => PAD + (1 - (value - lo) / (hi - lo)) * (plotHeight - PAD * 2);\n const rankOf = (field: \"start\" | \"end\") => {\n const sorted = [...data].sort((a, b) => b[field] - a[field]);\n return (item: SlopeItem) => sorted.indexOf(item) + 1;\n };\n const r0 = rankOf(\"start\");\n const r1 = rankOf(\"end\");\n const l0 = spread(data.map((item) => yOf(item.start)), LABEL_GAP, 8, plotHeight - 8);\n const l1 = spread(data.map((item) => yOf(item.end)), LABEL_GAP, 8, plotHeight - 8);\n const rows: Row[] = data.map((item, i) => ({\n item,\n y0: yOf(item.start),\n y1: yOf(item.end),\n l0: l0[i]!,\n l1: l1[i]!,\n rank0: r0(item),\n rank1: r1(item),\n order: 0,\n }));\n const byEnd = [...rows].sort((a, b) => a.rank1 - b.rank1);\n byEnd.forEach((row, k) => {\n row.order = k;\n });\n\n // The chart draws in its own pixels; the lives hold each row's four heights, moved by one spring and written by paint().\n const lives = useRef(new Map<string, RowLive>());\n const box = useRef({ x0, x1 });\n const paint = useCallback(() => {\n const root = plot.current;\n if (!root) return;\n const { x0, x1 } = box.current;\n for (const node of root.querySelectorAll<SVGGElement>(\"[data-row]\")) {\n const at = lives.current.get(node.dataset.row!);\n if (!at) continue;\n const line = node.querySelector(\"[data-part=line]\");\n line?.setAttribute(\"y1\", String(at.y0.value));\n line?.setAttribute(\"y2\", String(at.y1.value));\n node.querySelector(\"[data-part=dot0]\")?.setAttribute(\"cy\", String(at.y0.value));\n node.querySelector(\"[data-part=dot1]\")?.setAttribute(\"cy\", String(at.y1.value));\n node.querySelector(\"[data-part=lead0]\")?.setAttribute(\"d\", `M${x0 - 6},${at.l0.value}L${x0 - 1},${at.y0.value}`);\n node.querySelector(\"[data-part=lead1]\")?.setAttribute(\"d\", `M${x1 + 6},${at.l1.value}L${x1 + 1},${at.y1.value}`);\n }\n for (const node of root.querySelectorAll<HTMLElement>(\"[data-label]\")) {\n const at = lives.current.get(node.dataset.label!);\n if (at) node.style.translate = `0 ${node.dataset.side === \"end\" ? at.l1.value : at.l0.value}px`;\n }\n }, []);\n useIsomorphicLayoutEffect(() => {\n box.current = { x0, x1 };\n paint();\n });\n\n // New data moves every line, dot, leader and label from wherever it is on screen; a new item starts where it belongs.\n const signature = rows.map((row) => [row.item.key, row.y0, row.y1, row.l0, row.l1].map((part) => (typeof part === \"number\" ? Math.round(part * 100) : part)).join(\":\")).join(\"|\");\n const latest = useRef(rows);\n useIsomorphicLayoutEffect(() => {\n latest.current = rows;\n });\n useIsomorphicLayoutEffect(() => {\n const current = latest.current;\n const keys = new Set(current.map((row) => row.item.key));\n for (const key of [...lives.current.keys()]) {\n if (keys.has(key)) continue;\n const gone = lives.current.get(key)!;\n for (const value of Object.values(gone)) value.stop();\n lives.current.delete(key);\n }\n for (const row of current) {\n const at = lives.current.get(row.item.key);\n if (!at) {\n lives.current.set(row.item.key, { y0: live(row.y0), y1: live(row.y1), l0: live(row.l0), l1: live(row.l1) });\n continue;\n }\n retarget(at.y0, row.y0, MORPH, paint);\n retarget(at.y1, row.y1, MORPH, paint);\n retarget(at.l0, row.l0, MORPH, paint);\n retarget(at.l1, row.l1, MORPH, paint);\n }\n paint();\n // The signature stands for the rows; ready is the first width.\n \n }, [signature, paint, ready]);\n useEffect(\n () => () => {\n for (const at of lives.current.values()) for (const value of Object.values(at)) value.stop();\n },\n [],\n );\n\n // The lines draw once a third of the chart is in view.\n const [drawn, setDrawn] = useState(false);\n useEffect(() => {\n const node = figure.current;\n if (!node || prefersReducedMotion() || typeof IntersectionObserver === \"undefined\") {\n setDrawn(true);\n return;\n }\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (!entry?.isIntersecting) return;\n observer.disconnect();\n setDrawn(true);\n },\n { threshold: 0.3 },\n );\n observer.observe(node);\n return () => observer.disconnect();\n }, []);\n\n const [ownActive, setOwnActive] = useState<string | null>(null);\n const wanted = activeKey !== undefined ? activeKey : ownActive;\n const active = wanted && data.some((item) => item.key === wanted) ? wanted : null;\n const setActive = (key: string | null) => {\n if (activeKey === undefined) setOwnActive(key);\n if (key !== active) onActiveChange?.(key);\n };\n const activeRow = rows.find((row) => row.item.key === active) ?? null;\n // The tooltip keeps the last item's content while it fades out, so it never collapses to an empty box mid-fade.\n const [shownKey, setShownKey] = useState<string | null>(null);\n if (active && active !== shownKey) setShownKey(active);\n const tipRow = activeRow ?? rows.find((row) => row.item.key === shownKey) ?? null;\n\n // The tooltip sits over the middle of the line in focus and stays inside the chart; it glides between items once showing.\n const shownBefore = useRef(false);\n useIsomorphicLayoutEffect(() => {\n const node = tip.current;\n if (!activeRow || !node || !width) {\n shownBefore.current = false;\n return;\n }\n const tw = node.offsetWidth;\n const th = node.offsetHeight;\n const mid = (activeRow.y0 + activeRow.y1) / 2;\n const above = mid - th - 16 >= 0;\n const left = clamp((x0 + x1) / 2 - tw / 2, 0, Math.max(0, width - tw));\n const top = clamp(HEAD + (above ? mid - th - 16 : mid + 16), 0, Math.max(0, plotHeight + HEAD - th));\n if (!shownBefore.current) node.style.transition = \"none\";\n node.style.translate = `${left}px ${top}px`;\n if (!shownBefore.current) {\n void node.offsetWidth;\n node.style.transition = \"\";\n }\n shownBefore.current = true;\n });\n\n // The line nearest the pointer, measured at the pointer's position along the plot; a label counts as its end of the line.\n const nearest = (clientX: number, clientY: number) => {\n const rect = stage.current?.getBoundingClientRect();\n if (!rect || empty) return null;\n const px = clientX - rect.left;\n const y = clientY - rect.top - HEAD;\n const t = (clamp(px, x0, x1) - x0) / Math.max(1, x1 - x0);\n let best: Row | null = null;\n let distance = Infinity;\n for (const row of rows) {\n const inLine = Math.abs(row.y0 + (row.y1 - row.y0) * t - y);\n const onLabel = px < x0 ? Math.abs(row.l0 - y) : px > x1 ? Math.abs(row.l1 - y) : Infinity;\n const d = Math.min(inLine, onLabel);\n if (d < distance) {\n distance = d;\n best = row;\n }\n }\n return distance <= 22 ? (best?.item.key ?? null) : null;\n };\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (empty) return;\n if (event.key === \"Escape\" && active) {\n event.preventDefault();\n setActive(null);\n return;\n }\n const at = activeRow ? activeRow.order : -1;\n const moves: Record<string, number> = {\n ArrowDown: at + 1,\n ArrowRight: at + 1,\n ArrowUp: at < 0 ? 0 : at - 1,\n ArrowLeft: at < 0 ? 0 : at - 1,\n Home: 0,\n End: byEnd.length - 1,\n };\n if (!(event.key in moves)) return;\n event.preventDefault();\n setActive(byEnd[clamp(moves[event.key]!, 0, byEnd.length - 1)]!.item.key);\n };\n\n const format = (value: number) => (formatValue ? formatValue(value) : GROUPED.format(value));\n const change = (item: SlopeItem) =>\n formatChange ? formatChange(item.end - item.start, item) : `${item.end >= item.start ? \"+\" : \"−\"}${format(Math.abs(item.end - item.start))}`;\n const rankText = (row: Row) =>\n row.rank0 === row.rank1 ? \"same rank\" : row.rank1 < row.rank0 ? `up ${row.rank0 - row.rank1} to rank ${row.rank1}` : `down ${row.rank1 - row.rank0} to rank ${row.rank1}`;\n const describe = (row: Row) => `${row.item.label}, ${startLabel} ${format(row.item.start)}, ${endLabel} ${format(row.item.end)}, ${change(row.item)}, ${rankText(row)}`;\n const summary = empty\n ? `${label}. ${emptyLabel}.`\n : `${label}, ${startLabel} to ${endLabel}. ${byEnd.map((row) => `${row.item.label} ${format(row.item.start)} to ${format(row.item.end)}`).join(\", \")}.`;\n\n return (\n <figure\n ref={refs}\n data-slot=\"slope-chart\"\n data-drawn={drawn || undefined}\n data-scrubbing={active !== null || undefined}\n aria-label={label}\n className={cn(\"slope-chart box-border font-sans text-foreground\", className)}\n >\n <div\n ref={stage}\n data-slot=\"slope-chart-stage\"\n data-narrow={narrow || undefined}\n data-active={active ? \"\" : undefined}\n role={empty ? undefined : \"slider\"}\n tabIndex={empty ? undefined : 0}\n aria-label={empty ? undefined : `${label}, explore items in ${endLabel} order`}\n aria-valuemin={empty ? undefined : 1}\n aria-valuemax={empty ? undefined : data.length}\n aria-valuenow={empty ? undefined : (activeRow ? activeRow.order : 0) + 1}\n aria-valuetext={empty ? undefined : activeRow ? describe(activeRow) : summary}\n className=\"slope-chart-stage\"\n onPointerMove={(event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType === \"mouse\" || event.buttons) setActive(nearest(event.clientX, event.clientY));\n }}\n onPointerDown={(event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType === \"mouse\") return;\n event.currentTarget.setPointerCapture?.(event.pointerId);\n setActive(nearest(event.clientX, event.clientY));\n }}\n onPointerUp={(event) => {\n if (event.pointerType !== \"mouse\") setActive(null);\n }}\n onPointerCancel={() => setActive(null)}\n onPointerLeave={(event) => {\n if (event.pointerType === \"mouse\") setActive(null);\n }}\n onKeyDown={onKeyDown}\n onBlur={() => setActive(null)}\n onFocus={(event) => {\n if (event.currentTarget.matches(\":focus-visible\") && !empty && !active) setActive(byEnd[0]!.item.key);\n }}\n >\n {ready && !empty ? (\n <>\n <div aria-hidden=\"true\" className=\"relative h-7\">\n <span className=\"slope-chart-column\" style={{ left: x0 }}>\n {startLabel}\n </span>\n <span className=\"slope-chart-column\" style={{ left: x1 }}>\n {endLabel}\n </span>\n </div>\n <div ref={plot} className=\"relative\" style={{ height: plotHeight }}>\n <svg width={width} height={plotHeight} aria-hidden=\"true\" focusable=\"false\" className=\"block overflow-visible\">\n <line className=\"slope-chart-axis\" x1={x0} x2={x0} y1={0} y2={plotHeight} />\n <line className=\"slope-chart-axis\" x1={x1} x2={x1} y1={0} y2={plotHeight} />\n {rows.map((row, i) => {\n const key = row.item.key;\n return (\n <g\n key={key}\n data-row={key}\n data-on={active === key || undefined}\n data-accent={highlightKey === key || undefined}\n data-dim={(active !== null && active !== key) || undefined}\n className=\"slope-chart-item motion-safe:animate-fade-in\"\n style={{ \"--delay\": `${i * 50}ms` } as CSSProperties}\n >\n <path data-part=\"lead0\" className=\"slope-chart-leader\" d={`M${x0 - 6},${row.l0}L${x0 - 1},${row.y0}`} />\n <path data-part=\"lead1\" className=\"slope-chart-leader\" d={`M${x1 + 6},${row.l1}L${x1 + 1},${row.y1}`} />\n <line data-part=\"line\" className=\"slope-chart-line\" x1={x0} x2={x1} y1={row.y0} y2={row.y1} pathLength={1} />\n <circle data-part=\"dot0\" className=\"slope-chart-dot\" cx={x0} cy={row.y0} r={4} />\n <circle data-part=\"dot1\" data-end=\"\" className=\"slope-chart-dot\" cx={x1} cy={row.y1} r={4} />\n </g>\n );\n })}\n </svg>\n {rows.map((row, i) => {\n const key = row.item.key;\n const props = {\n \"data-label\": key,\n \"data-on\": active === key || undefined,\n \"data-accent\": highlightKey === key || undefined,\n className: \"slope-chart-label\",\n };\n const delay = { \"--delay\": `${i * 50}ms` };\n return (\n <div key={key} aria-hidden=\"true\">\n <span {...props} data-side=\"start\" style={{ ...delay, right: width - x0 + 10, maxWidth: x0 - 10 }}>\n <span className=\"slope-chart-name\">{row.item.label}</span>\n <span className=\"slope-chart-value\">{format(row.item.start)}</span>\n </span>\n <span {...props} data-side=\"end\" style={{ ...delay, left: x1 + 10 }}>\n <span className=\"slope-chart-value\">{format(row.item.end)}</span>\n {ranks ? <Rank move={row.rank0 - row.rank1} /> : null}\n </span>\n </div>\n );\n })}\n </div>\n </>\n ) : null}\n <div ref={tip} aria-hidden=\"true\" data-slot=\"slope-chart-tooltip\" className=\"line-chart-tooltip\">\n <p className=\"m-0 mb-0.5 whitespace-nowrap text-sm font-medium\">{tipRow?.item.label ?? \"\"}</p>\n <p className=\"m-0 flex items-center justify-between gap-3 text-sm\">\n <span className=\"text-text-secondary\">{startLabel}</span>\n <span className=\"font-medium tabular-nums\">{tipRow ? format(tipRow.item.start) : \"\"}</span>\n </p>\n <p className=\"m-0 flex items-center justify-between gap-3 text-sm\">\n <span className=\"text-text-secondary\">{endLabel}</span>\n <span className=\"font-medium tabular-nums\">{tipRow ? format(tipRow.item.end) : \"\"}</span>\n </p>\n <p className=\"m-0 mt-0.5 text-xs tabular-nums text-muted-foreground\">{tipRow ? `${change(tipRow.item)}, ${rankText(tipRow)}` : \"\"}</p>\n </div>\n {empty ? <p className=\"m-0 grid min-h-40 place-items-center text-sm text-muted-foreground\">{emptyLabel}</p> : null}\n </div>\n <p className=\"sr-only\">{summary}</p>\n <p className=\"sr-only\" aria-live=\"polite\" aria-atomic=\"true\">\n {activeRow ? describe(activeRow) : \"\"}\n </p>\n {empty ? null : (\n <div className=\"sr-only\">\n <table>\n <caption>{label}</caption>\n <thead>\n <tr>\n <th scope=\"col\">Item</th>\n <th scope=\"col\">{startLabel}</th>\n <th scope=\"col\">{endLabel}</th>\n <th scope=\"col\">Change</th>\n <th scope=\"col\">Rank</th>\n </tr>\n </thead>\n <tbody>\n {byEnd.map((row) => (\n <tr key={row.item.key}>\n <th scope=\"row\">{row.item.label}</th>\n <td>{format(row.item.start)}</td>\n <td>{format(row.item.end)}</td>\n <td>{change(row.item)}</td>\n <td>\n {row.rank0} to {row.rank1}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </figure>\n );\n});\n"],"mappings":"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"}
|