@oprt/react 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +105 -2
- package/dist/ActionStatus/ActionStatus.js +104 -0
- package/dist/ActionStatus/ActionStatus.manifest.js +64 -0
- package/dist/ActionStatus/index.js +7 -0
- package/dist/Alert/Alert.js +86 -0
- package/dist/Alert/Alert.manifest.js +36 -0
- package/dist/Alert/index.js +7 -0
- package/dist/AppShell/AppShell.js +103 -0
- package/dist/AppShell/AppShell.manifest.js +66 -0
- package/dist/AppShell/index.js +9 -0
- package/dist/AttachmentPreview/AttachmentPreview.js +145 -0
- package/dist/AttachmentPreview/AttachmentPreview.manifest.js +42 -0
- package/dist/AttachmentPreview/index.js +9 -0
- package/dist/Avatar/Avatar.js +87 -0
- package/dist/Avatar/Avatar.manifest.js +25 -0
- package/dist/Avatar/index.js +7 -0
- package/dist/Badge/Badge.js +124 -0
- package/dist/Badge/Badge.manifest.js +41 -0
- package/dist/Badge/index.js +7 -0
- package/dist/Breadcrumb/Breadcrumb.js +64 -0
- package/dist/Breadcrumb/Breadcrumb.manifest.js +28 -0
- package/dist/Breadcrumb/index.js +7 -0
- package/dist/BulkActionBar/BulkActionBar.js +145 -0
- package/dist/BulkActionBar/BulkActionBar.manifest.js +46 -0
- package/dist/BulkActionBar/index.js +9 -0
- package/dist/BulkActionBar/selection.js +44 -0
- package/dist/BulkResults/BulkResults.js +269 -0
- package/dist/BulkResults/BulkResults.manifest.js +45 -0
- package/dist/BulkResults/index.js +7 -0
- package/dist/Button/Button.js +170 -0
- package/dist/Button/Button.manifest.js +36 -0
- package/dist/Button/index.js +7 -0
- package/dist/ButtonGroup/ButtonGroup.js +60 -0
- package/dist/ButtonGroup/ButtonGroup.manifest.js +22 -0
- package/dist/ButtonGroup/index.js +7 -0
- package/dist/Calendar/Calendar.js +106 -0
- package/dist/Calendar/Calendar.manifest.js +32 -0
- package/dist/Calendar/index.js +7 -0
- package/dist/Card/Card.js +70 -0
- package/dist/Card/Card.manifest.js +57 -0
- package/dist/Card/index.js +16 -0
- package/dist/Chart/Chart.js +1200 -0
- package/dist/Chart/Chart.manifest.js +110 -0
- package/dist/Chart/ChartControls.js +190 -0
- package/dist/Chart/geometry.js +217 -0
- package/dist/Chart/index.js +9 -0
- package/dist/Checkbox/Checkbox.js +166 -0
- package/dist/Checkbox/Checkbox.manifest.js +38 -0
- package/dist/Checkbox/index.js +7 -0
- package/dist/Collapsible/Collapsible.js +177 -0
- package/dist/Collapsible/Collapsible.manifest.js +54 -0
- package/dist/Collapsible/index.js +12 -0
- package/dist/CollectionState/CollectionState.js +68 -0
- package/dist/CollectionState/CollectionState.manifest.js +44 -0
- package/dist/CollectionState/index.js +7 -0
- package/dist/ComboBox/ComboBox.js +221 -0
- package/dist/ComboBox/ComboBox.manifest.js +87 -0
- package/dist/ComboBox/index.js +7 -0
- package/dist/CommandPalette/CommandPalette.js +276 -0
- package/dist/CommandPalette/CommandPalette.manifest.js +24 -0
- package/dist/CommandPalette/index.js +7 -0
- package/dist/ConfirmDialog/ConfirmDialog.js +84 -0
- package/dist/ConfirmDialog/ConfirmDialog.manifest.js +32 -0
- package/dist/ConfirmDialog/index.js +7 -0
- package/dist/ContextMenu/ContextMenu.js +192 -0
- package/dist/ContextMenu/ContextMenu.manifest.js +41 -0
- package/dist/ContextMenu/index.js +7 -0
- package/dist/CoordinateInput/CoordinateInput.js +141 -0
- package/dist/CoordinateInput/CoordinateInput.manifest.js +36 -0
- package/dist/CoordinateInput/index.js +9 -0
- package/dist/CopyButton/CopyButton.js +110 -0
- package/dist/CopyButton/CopyButton.manifest.js +35 -0
- package/dist/CopyButton/index.js +7 -0
- package/dist/DataGrid/DataGrid.js +459 -0
- package/dist/DataGrid/DataGrid.manifest.js +135 -0
- package/dist/DataGrid/PageControls.js +41 -0
- package/dist/DataGrid/index.js +9 -0
- package/dist/DataTable/DataTable.js +375 -0
- package/dist/DataTable/DataTable.manifest.js +86 -0
- package/dist/DataTable/index.js +9 -0
- package/dist/DataTable/sort.js +45 -0
- package/dist/DataTable/useScrollsSideways.js +61 -0
- package/dist/DataTable/useTableWindow.js +185 -0
- package/dist/DatePicker/DatePicker.js +187 -0
- package/dist/DatePicker/DatePicker.manifest.js +40 -0
- package/dist/DatePicker/index.js +7 -0
- package/dist/DateRangePicker/DateRangePicker.js +156 -0
- package/dist/DateRangePicker/DateRangePicker.manifest.js +41 -0
- package/dist/DateRangePicker/index.js +7 -0
- package/dist/DateTimePicker/DateTimePicker.js +213 -0
- package/dist/DateTimePicker/DateTimePicker.manifest.js +44 -0
- package/dist/DateTimePicker/index.js +7 -0
- package/dist/DescriptionList/DescriptionList.js +93 -0
- package/dist/DescriptionList/DescriptionList.manifest.js +40 -0
- package/dist/DescriptionList/index.js +9 -0
- package/dist/Dialog/Dialog.js +214 -0
- package/dist/Dialog/Dialog.manifest.js +31 -0
- package/dist/Dialog/index.js +7 -0
- package/dist/Divider/Divider.js +64 -0
- package/dist/Divider/Divider.manifest.js +21 -0
- package/dist/Divider/index.js +7 -0
- package/dist/DropdownMenu/DropdownMenu.js +170 -0
- package/dist/DropdownMenu/DropdownMenu.manifest.js +82 -0
- package/dist/DropdownMenu/index.js +16 -0
- package/dist/EntityPreview/EntityPreview.js +82 -0
- package/dist/EntityPreview/EntityPreview.manifest.js +43 -0
- package/dist/EntityPreview/index.js +7 -0
- package/dist/EventTimeline/EventTimeline.js +249 -0
- package/dist/EventTimeline/EventTimeline.manifest.js +57 -0
- package/dist/EventTimeline/index.js +7 -0
- package/dist/Field/Field.js +81 -0
- package/dist/Field/Field.manifest.js +32 -0
- package/dist/Field/index.js +9 -0
- package/dist/FileUpload/FileUpload.js +215 -0
- package/dist/FileUpload/FileUpload.manifest.js +48 -0
- package/dist/FileUpload/index.js +7 -0
- package/dist/FilterBar/FilterBar.js +173 -0
- package/dist/FilterBar/FilterBar.manifest.js +49 -0
- package/dist/FilterBar/index.js +7 -0
- package/dist/FormErrorSummary/FormErrorSummary.js +70 -0
- package/dist/FormErrorSummary/FormErrorSummary.manifest.js +29 -0
- package/dist/FormErrorSummary/index.js +9 -0
- package/dist/FormSection/FormSection.js +51 -0
- package/dist/FormSection/FormSection.manifest.js +30 -0
- package/dist/FormSection/index.js +7 -0
- package/dist/InlineEdit/InlineEdit.js +261 -0
- package/dist/InlineEdit/InlineEdit.manifest.js +40 -0
- package/dist/InlineEdit/index.js +7 -0
- package/dist/Input/Input.js +126 -0
- package/dist/Input/Input.manifest.js +49 -0
- package/dist/Input/index.js +7 -0
- package/dist/Inspector/Inspector.js +63 -0
- package/dist/Inspector/Inspector.manifest.js +35 -0
- package/dist/Inspector/index.js +7 -0
- package/dist/JobStatus/JobStatus.js +118 -0
- package/dist/JobStatus/JobStatus.manifest.js +53 -0
- package/dist/JobStatus/index.js +7 -0
- package/dist/LayerControl/LayerControl.js +199 -0
- package/dist/LayerControl/LayerControl.manifest.js +34 -0
- package/dist/LayerControl/index.js +7 -0
- package/dist/Link/Link.js +83 -0
- package/dist/Link/Link.manifest.js +37 -0
- package/dist/Link/index.js +7 -0
- package/dist/MapControls/MapControls.js +122 -0
- package/dist/MapControls/MapControls.manifest.js +31 -0
- package/dist/MapControls/index.js +7 -0
- package/dist/MapLegend/MapLegend.js +89 -0
- package/dist/MapLegend/MapLegend.manifest.js +29 -0
- package/dist/MapLegend/index.js +7 -0
- package/dist/MapScale/MapScale.js +96 -0
- package/dist/MapScale/MapScale.manifest.js +31 -0
- package/dist/MapScale/index.js +7 -0
- package/dist/MediaFrame/MediaFrame.js +113 -0
- package/dist/MediaFrame/MediaFrame.manifest.js +57 -0
- package/dist/MediaFrame/index.js +7 -0
- package/dist/MultiSelect/MultiSelect.js +113 -0
- package/dist/MultiSelect/MultiSelect.manifest.js +51 -0
- package/dist/MultiSelect/index.js +7 -0
- package/dist/NumberInput/NumberInput.js +210 -0
- package/dist/NumberInput/NumberInput.manifest.js +56 -0
- package/dist/NumberInput/index.js +7 -0
- package/dist/OperationalStatus/OperationalStatus.js +89 -0
- package/dist/OperationalStatus/OperationalStatus.manifest.js +39 -0
- package/dist/OperationalStatus/index.js +9 -0
- package/dist/PlaybackControls/PlaybackControls.js +289 -0
- package/dist/PlaybackControls/PlaybackControls.manifest.js +85 -0
- package/dist/PlaybackControls/index.js +7 -0
- package/dist/Popover/Popover.js +302 -0
- package/dist/Popover/Popover.manifest.js +50 -0
- package/dist/Popover/index.js +7 -0
- package/dist/Progress/Progress.js +132 -0
- package/dist/Progress/Progress.manifest.js +30 -0
- package/dist/Progress/index.js +7 -0
- package/dist/PropertyFilter/PropertyFilter.js +306 -0
- package/dist/PropertyFilter/PropertyFilter.manifest.js +32 -0
- package/dist/PropertyFilter/index.js +11 -0
- package/dist/PropertyFilter/tokenError.js +17 -0
- package/dist/RadioGroup/RadioGroup.js +143 -0
- package/dist/RadioGroup/RadioGroup.manifest.js +44 -0
- package/dist/RadioGroup/index.js +9 -0
- package/dist/Select/Select.js +219 -0
- package/dist/Select/Select.manifest.js +77 -0
- package/dist/Select/index.js +9 -0
- package/dist/Sidebar/Sidebar.js +146 -0
- package/dist/Sidebar/Sidebar.manifest.js +72 -0
- package/dist/Sidebar/index.js +18 -0
- package/dist/Skeleton/Skeleton.js +76 -0
- package/dist/Skeleton/Skeleton.manifest.js +30 -0
- package/dist/Skeleton/index.js +7 -0
- package/dist/Slider/Slider.js +246 -0
- package/dist/Slider/Slider.manifest.js +56 -0
- package/dist/Slider/index.js +7 -0
- package/dist/Sparkline/Sparkline.js +135 -0
- package/dist/Sparkline/Sparkline.manifest.js +37 -0
- package/dist/Sparkline/index.js +7 -0
- package/dist/SplitPane/SplitPane.js +316 -0
- package/dist/SplitPane/SplitPane.manifest.js +79 -0
- package/dist/SplitPane/index.js +7 -0
- package/dist/StatCard/StatCard.js +163 -0
- package/dist/StatCard/StatCard.manifest.js +45 -0
- package/dist/StatCard/index.js +7 -0
- package/dist/Tabs/Tabs.js +181 -0
- package/dist/Tabs/Tabs.manifest.js +72 -0
- package/dist/Tabs/index.js +16 -0
- package/dist/Tabs/useTabsIndicator.js +71 -0
- package/dist/Tabs/useTabsScroll.js +123 -0
- package/dist/TagGroup/TagGroup.js +142 -0
- package/dist/TagGroup/TagGroup.manifest.js +35 -0
- package/dist/TagGroup/index.js +7 -0
- package/dist/Textarea/Textarea.js +68 -0
- package/dist/Textarea/Textarea.manifest.js +25 -0
- package/dist/Textarea/index.js +7 -0
- package/dist/TimeField/TimeField.js +118 -0
- package/dist/TimeField/TimeField.manifest.js +42 -0
- package/dist/TimeField/index.js +7 -0
- package/dist/TimeRange/TimeRange.js +184 -0
- package/dist/TimeRange/TimeRange.manifest.js +48 -0
- package/dist/TimeRange/index.js +9 -0
- package/dist/Toast/Toast.js +188 -0
- package/dist/Toast/Toast.manifest.js +77 -0
- package/dist/Toast/index.js +9 -0
- package/dist/Toggle/Toggle.js +154 -0
- package/dist/Toggle/Toggle.manifest.js +54 -0
- package/dist/Toggle/index.js +7 -0
- package/dist/Toolbar/Toolbar.js +86 -0
- package/dist/Toolbar/Toolbar.manifest.js +70 -0
- package/dist/Toolbar/index.js +12 -0
- package/dist/Tooltip/Tooltip.js +122 -0
- package/dist/Tooltip/Tooltip.manifest.js +27 -0
- package/dist/Tooltip/index.js +7 -0
- package/dist/TreeView/TreeView.js +129 -0
- package/dist/TreeView/TreeView.manifest.js +56 -0
- package/dist/TreeView/index.js +7 -0
- package/dist/UploadQueue/UploadQueue.js +296 -0
- package/dist/UploadQueue/UploadQueue.manifest.js +34 -0
- package/dist/UploadQueue/index.js +7 -0
- package/dist/_internal/MotionPopover.js +113 -0
- package/dist/_internal/aria.js +40 -0
- package/dist/_internal/bytes.js +18 -0
- package/dist/_internal/controlStyles.js +26 -0
- package/dist/_internal/date.js +179 -0
- package/dist/_internal/devValidate.js +51 -0
- package/dist/_internal/fieldIds.js +50 -0
- package/dist/_internal/hydration.js +74 -0
- package/dist/_internal/manifest-entry.js +0 -0
- package/dist/_internal/mergeRefs.js +68 -0
- package/dist/_internal/motion.js +138 -0
- package/dist/_internal/navigationLandmark.js +13 -0
- package/dist/_internal/numberText.js +176 -0
- package/dist/_internal/overlay.js +53 -0
- package/dist/_internal/segmentField.js +125 -0
- package/dist/_internal/suggestions.js +20 -0
- package/dist/_internal/timestamp.js +25 -0
- package/dist/_internal/unavailable.js +13 -0
- package/dist/_internal/useActionFocus.js +27 -0
- package/dist/_internal/useElementWidth.js +37 -0
- package/dist/_internal/useIsMounted.js +18 -0
- package/dist/_internal/useIsomorphicLayoutEffect.js +8 -0
- package/dist/_internal/useRestoreFocusOnRemoval.js +57 -0
- package/dist/index.cjs +17811 -0
- package/dist/index.d.cts +2330 -0
- package/dist/index.d.ts +2330 -0
- package/dist/index.js +407 -0
- package/dist/version.js +8 -0
- package/package.json +80 -3
|
@@ -0,0 +1,1200 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/Chart/Chart.tsx
|
|
4
|
+
import {
|
|
5
|
+
forwardRef,
|
|
6
|
+
memo,
|
|
7
|
+
useCallback,
|
|
8
|
+
useEffect,
|
|
9
|
+
useMemo,
|
|
10
|
+
useRef,
|
|
11
|
+
useState
|
|
12
|
+
} from "react";
|
|
13
|
+
import { useLocale, useNumberFormatter } from "react-aria";
|
|
14
|
+
import { cva } from "class-variance-authority";
|
|
15
|
+
import clsx from "clsx";
|
|
16
|
+
import { Button } from "../Button/Button.js";
|
|
17
|
+
import { devValidate } from "../_internal/devValidate.js";
|
|
18
|
+
import { usePartIds } from "../_internal/fieldIds.js";
|
|
19
|
+
import { NumberText } from "../_internal/numberText.js";
|
|
20
|
+
import { FocusableWhenUnavailableProvider } from "../_internal/unavailable.js";
|
|
21
|
+
import { useElementWidth } from "../_internal/useElementWidth.js";
|
|
22
|
+
import { ChartManifest } from "./Chart.manifest.js";
|
|
23
|
+
import { ChartInspection, ChartRangeSelector } from "./ChartControls.js";
|
|
24
|
+
import {
|
|
25
|
+
extent,
|
|
26
|
+
fraction,
|
|
27
|
+
indexByX,
|
|
28
|
+
interpolate,
|
|
29
|
+
intervalSeparator,
|
|
30
|
+
isFinitePoint,
|
|
31
|
+
linePath,
|
|
32
|
+
markerPoints,
|
|
33
|
+
nearestIndex,
|
|
34
|
+
niceAxis,
|
|
35
|
+
scale,
|
|
36
|
+
segments,
|
|
37
|
+
sequentialClasses,
|
|
38
|
+
seriesColorClass,
|
|
39
|
+
seriesIdentity,
|
|
40
|
+
tickLabels,
|
|
41
|
+
ticks,
|
|
42
|
+
withoutFloatNoise
|
|
43
|
+
} from "./geometry.js";
|
|
44
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
45
|
+
var chartStyles = cva(
|
|
46
|
+
"min-w-0 w-full border border-oprt-border bg-oprt-surface-1 p-3u font-sans text-oprt-text",
|
|
47
|
+
{
|
|
48
|
+
variants: { variant: { line: "", area: "", bar: "", scatter: "", histogram: "", heatmap: "" } },
|
|
49
|
+
defaultVariants: { variant: "line" }
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
var LEFT = 72;
|
|
53
|
+
var Y_LABEL_CHARACTER_WIDTH = (LEFT - 8) / 9;
|
|
54
|
+
var TOP = 16;
|
|
55
|
+
var BOTTOM = 236;
|
|
56
|
+
var HEIGHT = BOTTOM - TOP;
|
|
57
|
+
var SVG_HEIGHT = 270;
|
|
58
|
+
var LABEL_CHARACTER_WIDTH = 8;
|
|
59
|
+
var cellClass = "border-b border-oprt-border px-2u py-1u text-left";
|
|
60
|
+
var dashPatterns = [void 0, "7 3", "2 3", "8 3 2 3", "12 4", "4 2", "1 4", "9 2 1 2"];
|
|
61
|
+
var withUnit = (label, unit) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
62
|
+
label,
|
|
63
|
+
unit && /* @__PURE__ */ jsxs("span", { className: "normal-case tracking-normal", children: [
|
|
64
|
+
" (",
|
|
65
|
+
unit,
|
|
66
|
+
")"
|
|
67
|
+
] })
|
|
68
|
+
] });
|
|
69
|
+
var valueWithUnit = (value, unit) => `${value}${unit ? ` ${unit}` : ""}`;
|
|
70
|
+
var EXACT_NUMBER = { maximumSignificantDigits: 21 };
|
|
71
|
+
var CELL_NUMBER = { maximumSignificantDigits: 4 };
|
|
72
|
+
var colorClass = (item) => seriesColorClass(item.id, item.color);
|
|
73
|
+
var KEEP_COLORS = "forced-colors:[forced-color-adjust:none]";
|
|
74
|
+
var shortenLabel = (label, limit) => label.length > limit ? `${label.slice(0, Math.max(1, limit - 1))}\u2026` : label;
|
|
75
|
+
function Marker({
|
|
76
|
+
identity,
|
|
77
|
+
x,
|
|
78
|
+
y,
|
|
79
|
+
size = 3,
|
|
80
|
+
solid = false
|
|
81
|
+
}) {
|
|
82
|
+
const common = {
|
|
83
|
+
fill: solid ? "currentColor" : "var(--oprt-surface-1)",
|
|
84
|
+
stroke: "currentColor",
|
|
85
|
+
strokeWidth: "var(--oprt-chart-stroke-width)",
|
|
86
|
+
vectorEffect: "non-scaling-stroke"
|
|
87
|
+
};
|
|
88
|
+
switch (identity) {
|
|
89
|
+
case 1:
|
|
90
|
+
return /* @__PURE__ */ jsx("rect", { ...common, x: x - size, y: y - size, width: size * 2, height: size * 2 });
|
|
91
|
+
case 2:
|
|
92
|
+
return /* @__PURE__ */ jsx(
|
|
93
|
+
"path",
|
|
94
|
+
{
|
|
95
|
+
...common,
|
|
96
|
+
d: `M${x},${y - size * 1.4} L${x + size},${y} L${x},${y + size * 1.4} L${x - size},${y} Z`
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
case 3:
|
|
100
|
+
return /* @__PURE__ */ jsx(
|
|
101
|
+
"path",
|
|
102
|
+
{
|
|
103
|
+
...common,
|
|
104
|
+
d: `M${x},${y - size * 1.3} L${x + size * 1.2},${y + size} L${x - size * 1.2},${y + size} Z`
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
case 4:
|
|
108
|
+
return /* @__PURE__ */ jsx(
|
|
109
|
+
"path",
|
|
110
|
+
{
|
|
111
|
+
...common,
|
|
112
|
+
d: `M${x},${y + size * 1.3} L${x + size * 1.2},${y - size} L${x - size * 1.2},${y - size} Z`
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
case 5:
|
|
116
|
+
return /* @__PURE__ */ jsx(
|
|
117
|
+
"path",
|
|
118
|
+
{
|
|
119
|
+
...common,
|
|
120
|
+
d: `M${x - size},${y} H${x + size} M${x},${y - size} V${y + size}`,
|
|
121
|
+
fill: "none"
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
case 6:
|
|
125
|
+
return /* @__PURE__ */ jsx(
|
|
126
|
+
"path",
|
|
127
|
+
{
|
|
128
|
+
...common,
|
|
129
|
+
d: `M${x - size},${y - size} L${x + size},${y + size} M${x + size},${y - size} L${x - size},${y + size}`,
|
|
130
|
+
fill: "none"
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
case 7:
|
|
134
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
135
|
+
/* @__PURE__ */ jsx("circle", { ...common, cx: x, cy: y, r: size, fill: "var(--oprt-surface-1)" }),
|
|
136
|
+
solid && /* @__PURE__ */ jsx("circle", { cx: x, cy: y, r: 1, fill: "currentColor" })
|
|
137
|
+
] });
|
|
138
|
+
default:
|
|
139
|
+
return /* @__PURE__ */ jsx("circle", { ...common, cx: x, cy: y, r: size });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function histogram(series, bins) {
|
|
143
|
+
const finite = series.flatMap((item) => item.data.filter(isFinitePoint));
|
|
144
|
+
const domain = extent(finite.map((point) => point.y));
|
|
145
|
+
const count = domain.min === domain.max ? 1 : Number.isFinite(bins) ? Math.min(100, Math.max(1, Math.floor(bins))) : 10;
|
|
146
|
+
const edges = Array.from({ length: count + 1 }, (_, index) => interpolate(domain, index / count));
|
|
147
|
+
const counts = /* @__PURE__ */ new Map();
|
|
148
|
+
let missing = 0;
|
|
149
|
+
for (const item of series) {
|
|
150
|
+
const values = Array(count).fill(0);
|
|
151
|
+
for (const point of item.data) {
|
|
152
|
+
if (!isFinitePoint(point)) {
|
|
153
|
+
missing++;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
const index = Math.min(count - 1, Math.floor(fraction(point.y, domain) * count));
|
|
157
|
+
values[index] = (values[index] ?? 0) + 1;
|
|
158
|
+
}
|
|
159
|
+
counts.set(item.id, values);
|
|
160
|
+
}
|
|
161
|
+
return { domain, edges, counts, missing };
|
|
162
|
+
}
|
|
163
|
+
function binLabel(data, index, formatNumber, separator) {
|
|
164
|
+
const start = formatNumber(withoutFloatNoise(data.edges[index]));
|
|
165
|
+
const end = formatNumber(withoutFloatNoise(data.edges[index + 1]));
|
|
166
|
+
return start === end ? start : `[${start}${separator}${end}${index === data.edges.length - 2 ? "]" : ")"}`;
|
|
167
|
+
}
|
|
168
|
+
var DataView = memo(function DataView2({
|
|
169
|
+
id,
|
|
170
|
+
label,
|
|
171
|
+
series,
|
|
172
|
+
hidden,
|
|
173
|
+
distribution,
|
|
174
|
+
xHeading,
|
|
175
|
+
yHeading,
|
|
176
|
+
formatX,
|
|
177
|
+
formatNumber,
|
|
178
|
+
separator
|
|
179
|
+
}) {
|
|
180
|
+
return /* @__PURE__ */ jsx(
|
|
181
|
+
"div",
|
|
182
|
+
{
|
|
183
|
+
id,
|
|
184
|
+
className: "max-h-screen overflow-auto border border-oprt-border focus-visible:outline-hidden focus-visible:shadow-oprt-focus",
|
|
185
|
+
tabIndex: 0,
|
|
186
|
+
role: "region",
|
|
187
|
+
"aria-label": `${label} data table`,
|
|
188
|
+
children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-oprt-base", "aria-label": `${label} data`, children: [
|
|
189
|
+
/* @__PURE__ */ jsxs("caption", { className: "sr-only", children: [
|
|
190
|
+
label,
|
|
191
|
+
" data.",
|
|
192
|
+
" ",
|
|
193
|
+
distribution ? "Bin intervals include their lower boundary; the final bin also includes its upper boundary." : "All supplied samples, including hidden series. Missing means a null or non-finite value."
|
|
194
|
+
] }),
|
|
195
|
+
/* @__PURE__ */ jsx("thead", { className: "bg-oprt-surface-2 font-semibold uppercase tracking-oprt-caps", children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
196
|
+
/* @__PURE__ */ jsx("th", { scope: "col", className: cellClass, children: "Series" }),
|
|
197
|
+
/* @__PURE__ */ jsx("th", { scope: "col", className: cellClass, children: xHeading }),
|
|
198
|
+
/* @__PURE__ */ jsx("th", { scope: "col", className: cellClass, children: yHeading }),
|
|
199
|
+
/* @__PURE__ */ jsx("th", { scope: "col", className: cellClass, children: "Visibility" })
|
|
200
|
+
] }) }),
|
|
201
|
+
/* @__PURE__ */ jsx("tbody", { children: series.flatMap(
|
|
202
|
+
(item) => distribution ? (distribution.counts.get(item.id) ?? []).map((count, index) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
203
|
+
/* @__PURE__ */ jsx("th", { scope: "row", className: clsx(cellClass, "font-medium"), children: item.label }),
|
|
204
|
+
/* @__PURE__ */ jsx("td", { className: clsx(cellClass, "font-mono"), children: binLabel(distribution, index, formatNumber, separator) }),
|
|
205
|
+
/* @__PURE__ */ jsx("td", { className: clsx(cellClass, "font-mono"), children: formatNumber(count) }),
|
|
206
|
+
/* @__PURE__ */ jsx("td", { className: cellClass, children: hidden.has(item.id) ? "Hidden" : "Visible" })
|
|
207
|
+
] }, `${item.id}:${index}`)) : item.data.map((point, index) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
208
|
+
/* @__PURE__ */ jsx("th", { scope: "row", className: clsx(cellClass, "font-medium"), children: item.label }),
|
|
209
|
+
/* @__PURE__ */ jsx("td", { className: clsx(cellClass, "font-mono"), children: Number.isFinite(point.x) ? point.label ?? formatX?.(point.x) ?? formatNumber(point.x) : "Missing" }),
|
|
210
|
+
/* @__PURE__ */ jsx("td", { className: clsx(cellClass, "font-mono"), children: point.y !== null && Number.isFinite(point.y) ? formatNumber(point.y) : "Missing" }),
|
|
211
|
+
/* @__PURE__ */ jsx("td", { className: cellClass, children: hidden.has(item.id) ? "Hidden" : "Visible" })
|
|
212
|
+
] }, `${item.id}:${index}`))
|
|
213
|
+
) })
|
|
214
|
+
] })
|
|
215
|
+
}
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
function seriesMarks(item, seriesIndex, samples, context) {
|
|
219
|
+
const {
|
|
220
|
+
variant,
|
|
221
|
+
fill,
|
|
222
|
+
hatchId,
|
|
223
|
+
geometry: { left, x, y, baseline, width, groupWidth },
|
|
224
|
+
visibleCount,
|
|
225
|
+
distribution,
|
|
226
|
+
xValues,
|
|
227
|
+
heatDomain,
|
|
228
|
+
unit,
|
|
229
|
+
formatX,
|
|
230
|
+
formatNumber,
|
|
231
|
+
formatCell,
|
|
232
|
+
separator
|
|
233
|
+
} = context;
|
|
234
|
+
const identity = seriesIdentity(item.id);
|
|
235
|
+
if (variant === "histogram" && distribution) {
|
|
236
|
+
const values = distribution.counts.get(item.id) ?? [];
|
|
237
|
+
const binWidth = width / Math.max(1, values.length);
|
|
238
|
+
const barWidth = binWidth * 0.8 / Math.max(1, visibleCount);
|
|
239
|
+
return values.map((count, index) => /* @__PURE__ */ jsxs("g", { children: [
|
|
240
|
+
/* @__PURE__ */ jsx(
|
|
241
|
+
"rect",
|
|
242
|
+
{
|
|
243
|
+
x: left + index * binWidth + binWidth * 0.1 + seriesIndex * barWidth,
|
|
244
|
+
y: Math.min(y(count), baseline),
|
|
245
|
+
width: barWidth,
|
|
246
|
+
height: Math.abs(y(count) - baseline),
|
|
247
|
+
fill: "currentColor",
|
|
248
|
+
fillOpacity: "var(--oprt-chart-bar-opacity)",
|
|
249
|
+
stroke: "currentColor",
|
|
250
|
+
strokeWidth: "var(--oprt-chart-stroke-width)",
|
|
251
|
+
vectorEffect: "non-scaling-stroke"
|
|
252
|
+
}
|
|
253
|
+
),
|
|
254
|
+
count > 0 && /* @__PURE__ */ jsx(
|
|
255
|
+
Marker,
|
|
256
|
+
{
|
|
257
|
+
identity,
|
|
258
|
+
x: left + index * binWidth + binWidth * 0.1 + (seriesIndex + 0.5) * barWidth,
|
|
259
|
+
y: y(count),
|
|
260
|
+
size: Math.min(3, barWidth / 3)
|
|
261
|
+
}
|
|
262
|
+
),
|
|
263
|
+
/* @__PURE__ */ jsx("title", { children: `${item.label}: ${binLabel(distribution, index, formatNumber, separator)}, ${formatNumber(count)} samples` })
|
|
264
|
+
] }, index));
|
|
265
|
+
}
|
|
266
|
+
if (variant === "bar") {
|
|
267
|
+
const barWidth = groupWidth / Math.max(1, visibleCount);
|
|
268
|
+
return item.data.filter(isFinitePoint).map((point, index) => /* @__PURE__ */ jsxs("g", { "data-chart-point": "", children: [
|
|
269
|
+
/* @__PURE__ */ jsx(
|
|
270
|
+
"rect",
|
|
271
|
+
{
|
|
272
|
+
x: x(point.x) - groupWidth / 2 + barWidth * seriesIndex,
|
|
273
|
+
y: Math.min(y(point.y), baseline),
|
|
274
|
+
width: barWidth * 0.9,
|
|
275
|
+
height: Math.abs(y(point.y) - baseline),
|
|
276
|
+
fill: fill === "hatched" ? `url(#${hatchId})` : "currentColor",
|
|
277
|
+
fillOpacity: fill === "hatched" ? 1 : "var(--oprt-chart-bar-opacity)",
|
|
278
|
+
stroke: "currentColor",
|
|
279
|
+
strokeWidth: "var(--oprt-chart-stroke-width)",
|
|
280
|
+
vectorEffect: "non-scaling-stroke"
|
|
281
|
+
}
|
|
282
|
+
),
|
|
283
|
+
/* @__PURE__ */ jsx(
|
|
284
|
+
Marker,
|
|
285
|
+
{
|
|
286
|
+
identity,
|
|
287
|
+
x: x(point.x) - groupWidth / 2 + barWidth * (seriesIndex + 0.45),
|
|
288
|
+
y: y(point.y),
|
|
289
|
+
size: Math.min(3, barWidth / 3)
|
|
290
|
+
}
|
|
291
|
+
),
|
|
292
|
+
/* @__PURE__ */ jsx("title", { children: `${item.label}: ${point.label ?? formatX?.(point.x) ?? formatNumber(point.x)}, ${valueWithUnit(formatNumber(point.y), unit)}` })
|
|
293
|
+
] }, index));
|
|
294
|
+
}
|
|
295
|
+
if (variant === "heatmap") {
|
|
296
|
+
const cellWidth = width / Math.max(1, xValues.length);
|
|
297
|
+
const cellHeight = HEIGHT / Math.max(1, visibleCount);
|
|
298
|
+
return xValues.map((value, index) => {
|
|
299
|
+
const point = samples?.get(value);
|
|
300
|
+
const valid2 = point && isFinitePoint(point);
|
|
301
|
+
const className = valid2 ? sequentialClasses[Math.min(4, Math.floor(fraction(point.y, heatDomain) * 5))] : "text-oprt-chart-grid";
|
|
302
|
+
const cellX = left + index * cellWidth;
|
|
303
|
+
const cellY = TOP + seriesIndex * cellHeight;
|
|
304
|
+
return /* @__PURE__ */ jsxs("g", { "data-chart-cell": "", "data-chart-missing": valid2 ? void 0 : "", children: [
|
|
305
|
+
/* @__PURE__ */ jsx(
|
|
306
|
+
"rect",
|
|
307
|
+
{
|
|
308
|
+
x: cellX + 1,
|
|
309
|
+
y: cellY + 1,
|
|
310
|
+
width: Math.max(0, cellWidth - 2),
|
|
311
|
+
height: Math.max(0, cellHeight - 2),
|
|
312
|
+
className: clsx(className, valid2 && KEEP_COLORS),
|
|
313
|
+
fill: valid2 ? "currentColor" : "none",
|
|
314
|
+
stroke: valid2 ? "none" : "currentColor"
|
|
315
|
+
}
|
|
316
|
+
),
|
|
317
|
+
xValues.length <= 12 && visibleCount <= 8 && cellHeight >= 24 && (valid2 ? formatCell(point.y).length : 1) * LABEL_CHARACTER_WIDTH + 8 <= cellWidth && /* @__PURE__ */ jsx(
|
|
318
|
+
"text",
|
|
319
|
+
{
|
|
320
|
+
x: cellX + cellWidth / 2,
|
|
321
|
+
y: cellY + cellHeight / 2,
|
|
322
|
+
dominantBaseline: "middle",
|
|
323
|
+
textAnchor: "middle",
|
|
324
|
+
className: clsx(
|
|
325
|
+
"font-mono text-oprt-base",
|
|
326
|
+
valid2 ? clsx("fill-current text-oprt-bg", KEEP_COLORS) : "fill-current text-oprt-text-dim"
|
|
327
|
+
),
|
|
328
|
+
children: valid2 ? formatCell(point.y) : "\u2014"
|
|
329
|
+
}
|
|
330
|
+
),
|
|
331
|
+
/* @__PURE__ */ jsx("title", { children: `${item.label}: ${point?.label ?? formatX?.(value) ?? formatNumber(value)}, ${valid2 ? valueWithUnit(formatNumber(point.y), unit) : "Missing"}` })
|
|
332
|
+
] }, value);
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
const valid = item.data.filter(isFinitePoint);
|
|
336
|
+
if (variant === "scatter") {
|
|
337
|
+
return valid.map((point, index) => /* @__PURE__ */ jsxs("g", { "data-chart-point": "", children: [
|
|
338
|
+
/* @__PURE__ */ jsx("circle", { cx: x(point.x), cy: y(point.y), r: 8, fill: "transparent" }),
|
|
339
|
+
/* @__PURE__ */ jsx("g", { "data-chart-marker": "", children: /* @__PURE__ */ jsx(Marker, { identity, x: x(point.x), y: y(point.y), size: 3.5 }) }),
|
|
340
|
+
/* @__PURE__ */ jsx("title", { children: `${item.label}: ${point.label ?? formatX?.(point.x) ?? formatNumber(point.x)}, ${valueWithUnit(formatNumber(point.y), unit)}` })
|
|
341
|
+
] }, index));
|
|
342
|
+
}
|
|
343
|
+
const runs = segments(item.data);
|
|
344
|
+
const last = valid[valid.length - 1];
|
|
345
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
346
|
+
runs.map((segment, index) => {
|
|
347
|
+
const path = linePath(segment, x, y);
|
|
348
|
+
return /* @__PURE__ */ jsxs("g", { "data-chart-segment": item.id, children: [
|
|
349
|
+
variant === "area" && /* @__PURE__ */ jsx(
|
|
350
|
+
"path",
|
|
351
|
+
{
|
|
352
|
+
d: `${path} L${x(segment[segment.length - 1].x)},${baseline} L${x(segment[0].x)},${baseline} Z`,
|
|
353
|
+
fill: "currentColor",
|
|
354
|
+
fillOpacity: "var(--oprt-chart-area-opacity)"
|
|
355
|
+
}
|
|
356
|
+
),
|
|
357
|
+
variant === "area" && fill === "hatched" && /* @__PURE__ */ jsx(
|
|
358
|
+
"path",
|
|
359
|
+
{
|
|
360
|
+
d: `${path} L${x(segment[segment.length - 1].x)},${baseline} L${x(segment[0].x)},${baseline} Z`,
|
|
361
|
+
fill: `url(#${hatchId})`
|
|
362
|
+
}
|
|
363
|
+
),
|
|
364
|
+
/* @__PURE__ */ jsx(
|
|
365
|
+
"path",
|
|
366
|
+
{
|
|
367
|
+
"data-chart-line": "",
|
|
368
|
+
d: path,
|
|
369
|
+
fill: "none",
|
|
370
|
+
stroke: "currentColor",
|
|
371
|
+
strokeWidth: "var(--oprt-chart-stroke-width)",
|
|
372
|
+
strokeDasharray: dashPatterns[identity],
|
|
373
|
+
strokeLinecap: "butt",
|
|
374
|
+
strokeLinejoin: "miter",
|
|
375
|
+
strokeMiterlimit: 2,
|
|
376
|
+
vectorEffect: "non-scaling-stroke"
|
|
377
|
+
}
|
|
378
|
+
)
|
|
379
|
+
] }, index);
|
|
380
|
+
}),
|
|
381
|
+
[...markerPoints(runs, x)].map((point, index) => /* @__PURE__ */ jsx("g", { "data-chart-marker": "", children: /* @__PURE__ */ jsx(
|
|
382
|
+
Marker,
|
|
383
|
+
{
|
|
384
|
+
identity,
|
|
385
|
+
x: x(point.x),
|
|
386
|
+
y: y(point.y),
|
|
387
|
+
size: 2.5,
|
|
388
|
+
solid: point === last
|
|
389
|
+
}
|
|
390
|
+
) }, index))
|
|
391
|
+
] });
|
|
392
|
+
}
|
|
393
|
+
var Chart = forwardRef((allProps, ref) => {
|
|
394
|
+
const {
|
|
395
|
+
label,
|
|
396
|
+
series,
|
|
397
|
+
variant = "line",
|
|
398
|
+
fill = "solid",
|
|
399
|
+
description,
|
|
400
|
+
xLabel,
|
|
401
|
+
xUnit,
|
|
402
|
+
yLabel,
|
|
403
|
+
unit,
|
|
404
|
+
hiddenSeries,
|
|
405
|
+
onHiddenSeriesChange,
|
|
406
|
+
disabled = false,
|
|
407
|
+
bins = 10,
|
|
408
|
+
formatX,
|
|
409
|
+
threshold,
|
|
410
|
+
rangeSelection = false,
|
|
411
|
+
xDomain: suppliedXDomain,
|
|
412
|
+
range,
|
|
413
|
+
onRangeChange,
|
|
414
|
+
cursor,
|
|
415
|
+
cursorLabel = "Review position",
|
|
416
|
+
className,
|
|
417
|
+
...props
|
|
418
|
+
} = allProps;
|
|
419
|
+
if (process.env.NODE_ENV !== "production") {
|
|
420
|
+
devValidate("Chart", ChartManifest.Chart.props, { ...allProps });
|
|
421
|
+
}
|
|
422
|
+
const labelRef = useRef(null);
|
|
423
|
+
const { locale } = useLocale();
|
|
424
|
+
const numberFormat = useNumberFormatter(EXACT_NUMBER);
|
|
425
|
+
const separator = useMemo(() => intervalSeparator(locale), [locale]);
|
|
426
|
+
const formatNumber = useCallback(
|
|
427
|
+
(value) => numberFormat.format(value + 0),
|
|
428
|
+
[numberFormat]
|
|
429
|
+
);
|
|
430
|
+
const cellFormat = useNumberFormatter(CELL_NUMBER);
|
|
431
|
+
const formatCell = useCallback((value) => cellFormat.format(value + 0), [cellFormat]);
|
|
432
|
+
const ids = usePartIds(["label", "description", "hatch", "data"], {
|
|
433
|
+
readRendered: () => labelRef.current?.id.replace(/-label$/, "")
|
|
434
|
+
});
|
|
435
|
+
const plotRef = useRef(null);
|
|
436
|
+
const plotWidth = useElementWidth(plotRef, { initial: 640, min: 0 });
|
|
437
|
+
const [localHidden, setLocalHidden] = useState([]);
|
|
438
|
+
const [showData, setShowData] = useState(false);
|
|
439
|
+
const [localRange, setLocalRange] = useState();
|
|
440
|
+
const [inspection, setInspection] = useState(null);
|
|
441
|
+
const [pinned, setPinned] = useState(false);
|
|
442
|
+
const [inspectionFocused, setInspectionFocused] = useState(false);
|
|
443
|
+
const [emphasized, setEmphasized] = useState(null);
|
|
444
|
+
const inspectionRef = useRef(null);
|
|
445
|
+
const hiddenKey = JSON.stringify(hiddenSeries ?? localHidden);
|
|
446
|
+
const hidden = useMemo(() => new Set(JSON.parse(hiddenKey)), [hiddenKey]);
|
|
447
|
+
const inspectable = variant === "line" || variant === "area" || variant === "bar";
|
|
448
|
+
const fullVisible = useMemo(
|
|
449
|
+
() => series.filter((item) => !hidden.has(item.id)),
|
|
450
|
+
[series, hidden]
|
|
451
|
+
);
|
|
452
|
+
const dataDomain = useMemo(
|
|
453
|
+
() => extent(series.flatMap((item) => item.data.map((point) => point.x))),
|
|
454
|
+
[series]
|
|
455
|
+
);
|
|
456
|
+
const suppliedStart = suppliedXDomain?.start;
|
|
457
|
+
const suppliedEnd = suppliedXDomain?.end;
|
|
458
|
+
const fullDomain = useMemo(
|
|
459
|
+
() => inspectable && suppliedStart !== void 0 && suppliedEnd !== void 0 && Number.isFinite(suppliedStart) && Number.isFinite(suppliedEnd) && suppliedStart < suppliedEnd ? { min: suppliedStart, max: suppliedEnd } : dataDomain,
|
|
460
|
+
[inspectable, suppliedStart, suppliedEnd, dataDomain]
|
|
461
|
+
);
|
|
462
|
+
const requested = range ?? localRange;
|
|
463
|
+
const requestedStart = requested?.start;
|
|
464
|
+
const requestedEnd = requested?.end;
|
|
465
|
+
const controlledRange = range !== void 0;
|
|
466
|
+
const view = useMemo(() => {
|
|
467
|
+
const full = { start: fullDomain.min, end: fullDomain.max, valid: false };
|
|
468
|
+
if (!inspectable || requestedStart === void 0 || requestedEnd === void 0) return full;
|
|
469
|
+
if (!Number.isFinite(requestedStart) || !Number.isFinite(requestedEnd) || requestedStart >= requestedEnd)
|
|
470
|
+
return full;
|
|
471
|
+
if (requestedEnd >= fullDomain.min && requestedStart <= fullDomain.max) {
|
|
472
|
+
return {
|
|
473
|
+
start: Math.max(fullDomain.min, requestedStart),
|
|
474
|
+
end: Math.min(fullDomain.max, requestedEnd),
|
|
475
|
+
valid: true
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
return controlledRange ? { start: requestedStart, end: requestedEnd, valid: true } : { ...full, valid: true };
|
|
479
|
+
}, [inspectable, requestedStart, requestedEnd, controlledRange, fullDomain]);
|
|
480
|
+
const narrowed = inspectable && (view.start > dataDomain.min || view.end < dataDomain.max);
|
|
481
|
+
const visible = useMemo(
|
|
482
|
+
() => narrowed ? fullVisible.map((item) => ({
|
|
483
|
+
...item,
|
|
484
|
+
data: item.data.filter(
|
|
485
|
+
(point) => !Number.isFinite(point.x) || point.x >= view.start && point.x <= view.end
|
|
486
|
+
)
|
|
487
|
+
})) : fullVisible,
|
|
488
|
+
[narrowed, fullVisible, view.start, view.end]
|
|
489
|
+
);
|
|
490
|
+
const samplesAt = useMemo(
|
|
491
|
+
() => new Map(visible.map((item) => [item, indexByX(item.data)])),
|
|
492
|
+
[visible]
|
|
493
|
+
);
|
|
494
|
+
const finite = useMemo(
|
|
495
|
+
() => visible.flatMap((item) => item.data.filter(isFinitePoint)),
|
|
496
|
+
[visible]
|
|
497
|
+
);
|
|
498
|
+
const distribution = useMemo(
|
|
499
|
+
() => variant === "histogram" ? histogram(visible, bins) : void 0,
|
|
500
|
+
[variant, visible, bins]
|
|
501
|
+
);
|
|
502
|
+
const thresholdValue = variant !== "heatmap" && threshold && Number.isFinite(threshold.value) ? threshold.value : void 0;
|
|
503
|
+
const yAxis = useMemo(() => {
|
|
504
|
+
const values = distribution ? [...distribution.counts.values()].flat() : finite.map((point) => point.y);
|
|
505
|
+
if (thresholdValue !== void 0) values.push(thresholdValue);
|
|
506
|
+
return niceAxis(
|
|
507
|
+
extent(values, variant === "bar" || variant === "area" || variant === "histogram"),
|
|
508
|
+
variant === "histogram"
|
|
509
|
+
);
|
|
510
|
+
}, [distribution, finite, thresholdValue, variant]);
|
|
511
|
+
const xValues = useMemo(
|
|
512
|
+
() => [
|
|
513
|
+
...new Set(
|
|
514
|
+
visible.flatMap((item) => item.data.map((point) => point.x)).filter(Number.isFinite)
|
|
515
|
+
)
|
|
516
|
+
].sort((a, b) => a - b),
|
|
517
|
+
[visible]
|
|
518
|
+
);
|
|
519
|
+
const windowedAxis = inspectable && (rangeSelection || view.valid || suppliedXDomain !== void 0);
|
|
520
|
+
const xDomain = useMemo(
|
|
521
|
+
() => windowedAxis ? { min: view.start, max: view.end } : distribution?.domain ?? (xValues.length > 0 ? { min: xValues[0], max: xValues[xValues.length - 1] } : extent([])),
|
|
522
|
+
[windowedAxis, view.start, view.end, distribution, xValues]
|
|
523
|
+
);
|
|
524
|
+
const heatDomain = useMemo(
|
|
525
|
+
() => variant === "heatmap" ? extent(finite.map((point) => point.y)) : dataDomain,
|
|
526
|
+
[variant, finite, dataDomain]
|
|
527
|
+
);
|
|
528
|
+
const anyFiniteX = useMemo(
|
|
529
|
+
() => series.some((item) => item.data.some((point) => Number.isFinite(point.x))),
|
|
530
|
+
[series]
|
|
531
|
+
);
|
|
532
|
+
const missing = useMemo(
|
|
533
|
+
() => visible.reduce(
|
|
534
|
+
(total, item) => total + item.data.filter((point) => !isFinitePoint(point)).length,
|
|
535
|
+
0
|
|
536
|
+
),
|
|
537
|
+
[visible]
|
|
538
|
+
);
|
|
539
|
+
const histogramView = distribution !== void 0;
|
|
540
|
+
const xHeading = useMemo(
|
|
541
|
+
() => histogramView ? withUnit(xLabel ?? "Value", unit) : withUnit(xLabel ?? "Sample", xUnit),
|
|
542
|
+
[histogramView, xLabel, xUnit, unit]
|
|
543
|
+
);
|
|
544
|
+
const yHeading = useMemo(
|
|
545
|
+
() => histogramView ? yLabel ?? "Count" : withUnit(yLabel ?? "Value", unit),
|
|
546
|
+
[histogramView, yLabel, unit]
|
|
547
|
+
);
|
|
548
|
+
const empty = series.length === 0 ? "No data to display." : visible.length === 0 ? "All series are hidden." : narrowed && xValues.length === 0 ? "No samples in this range." : finite.length === 0 ? "No finite data to display." : void 0;
|
|
549
|
+
const yLabels = useMemo(
|
|
550
|
+
() => variant === "heatmap" ? [] : tickLabels(yAxis.ticks, locale),
|
|
551
|
+
[variant, yAxis, locale]
|
|
552
|
+
);
|
|
553
|
+
const longestYLabel = Math.max(9, ...yLabels.map((label2) => label2.length));
|
|
554
|
+
const left = Math.max(LEFT, Math.ceil(8 + longestYLabel * Y_LABEL_CHARACTER_WIDTH));
|
|
555
|
+
const geometry = useMemo(() => {
|
|
556
|
+
const right = Math.max(left + 1, plotWidth - 24);
|
|
557
|
+
const width = right - left;
|
|
558
|
+
const initialX = scale(xDomain, left, right);
|
|
559
|
+
let slotWidth = width;
|
|
560
|
+
for (let index = 1; index < xValues.length; index++)
|
|
561
|
+
slotWidth = Math.min(slotWidth, initialX(xValues[index]) - initialX(xValues[index - 1]));
|
|
562
|
+
const groupWidth = Math.min(48, slotWidth * 0.7);
|
|
563
|
+
const inset = variant === "bar" ? groupWidth / 2 : 0;
|
|
564
|
+
const y2 = scale(yAxis.domain, BOTTOM, TOP);
|
|
565
|
+
return {
|
|
566
|
+
left,
|
|
567
|
+
right,
|
|
568
|
+
width,
|
|
569
|
+
groupWidth,
|
|
570
|
+
x: scale(xDomain, left + inset, right - inset),
|
|
571
|
+
y: y2,
|
|
572
|
+
baseline: y2(0)
|
|
573
|
+
};
|
|
574
|
+
}, [left, plotWidth, xDomain, xValues, variant, yAxis]);
|
|
575
|
+
const xTickLabels = useMemo(() => {
|
|
576
|
+
const { left: left2, width, x: x2 } = geometry;
|
|
577
|
+
const textFor = (value, interpolated) => formatX && !histogramView ? formatX(value) : formatNumber(interpolated ? withoutFloatNoise(value) : value);
|
|
578
|
+
const columnText = variant === "heatmap" && xValues.length <= 6 ? xValues.map((value) => textFor(value, false)) : [];
|
|
579
|
+
const heatmapTicksFit = columnText.length > 0 && columnText.every(
|
|
580
|
+
(text) => text.length * LABEL_CHARACTER_WIDTH + 16 <= width / Math.max(1, xValues.length)
|
|
581
|
+
);
|
|
582
|
+
const xTicks = variant === "heatmap" ? xValues.flatMap(
|
|
583
|
+
(value, index) => heatmapTicksFit || index === 0 || index === xValues.length - 1 ? [
|
|
584
|
+
{
|
|
585
|
+
value,
|
|
586
|
+
position: left2 + (index + 0.5) * width / xValues.length,
|
|
587
|
+
interpolated: false
|
|
588
|
+
}
|
|
589
|
+
] : []
|
|
590
|
+
) : ticks(xDomain, width < 384 ? 2 : 3).map((value, index, all) => ({
|
|
591
|
+
value,
|
|
592
|
+
position: x2(value),
|
|
593
|
+
interpolated: index > 0 && index < all.length - 1
|
|
594
|
+
}));
|
|
595
|
+
const texts = xTicks.map((tick) => textFor(tick.value, tick.interpolated));
|
|
596
|
+
return xTicks.map(({ position }, index) => {
|
|
597
|
+
const start = index === 0 ? 0 : Math.min(plotWidth, (xTicks[index - 1].position + position) / 2 + 4);
|
|
598
|
+
const end = index === xTicks.length - 1 ? plotWidth : Math.min(plotWidth, (position + xTicks[index + 1].position) / 2 - 4);
|
|
599
|
+
const text = texts[index];
|
|
600
|
+
const shortText = shortenLabel(
|
|
601
|
+
text,
|
|
602
|
+
Math.max(1, Math.floor((end - start) / LABEL_CHARACTER_WIDTH))
|
|
603
|
+
);
|
|
604
|
+
const halfWidth = shortText.length * LABEL_CHARACTER_WIDTH / 2;
|
|
605
|
+
return {
|
|
606
|
+
text,
|
|
607
|
+
shortText,
|
|
608
|
+
x: Math.max(start + halfWidth, Math.min(end - halfWidth, position))
|
|
609
|
+
};
|
|
610
|
+
});
|
|
611
|
+
}, [geometry, histogramView, formatX, formatNumber, xValues, variant, xDomain, plotWidth]);
|
|
612
|
+
const hatchId = ids.hatch;
|
|
613
|
+
const marksLayer = useMemo(() => {
|
|
614
|
+
const context = {
|
|
615
|
+
variant,
|
|
616
|
+
fill,
|
|
617
|
+
hatchId,
|
|
618
|
+
geometry,
|
|
619
|
+
visibleCount: visible.length,
|
|
620
|
+
distribution,
|
|
621
|
+
xValues,
|
|
622
|
+
heatDomain,
|
|
623
|
+
unit,
|
|
624
|
+
formatX,
|
|
625
|
+
formatNumber,
|
|
626
|
+
formatCell,
|
|
627
|
+
separator
|
|
628
|
+
};
|
|
629
|
+
return visible.map((item, index) => /* @__PURE__ */ jsx("g", { "data-chart-series": item.id, className: colorClass(item), children: seriesMarks(item, index, samplesAt.get(item), context) }));
|
|
630
|
+
}, [
|
|
631
|
+
variant,
|
|
632
|
+
fill,
|
|
633
|
+
hatchId,
|
|
634
|
+
geometry,
|
|
635
|
+
visible,
|
|
636
|
+
distribution,
|
|
637
|
+
xValues,
|
|
638
|
+
heatDomain,
|
|
639
|
+
unit,
|
|
640
|
+
formatX,
|
|
641
|
+
formatNumber,
|
|
642
|
+
formatCell,
|
|
643
|
+
separator,
|
|
644
|
+
samplesAt
|
|
645
|
+
]);
|
|
646
|
+
const seriesNumber = useMemo(
|
|
647
|
+
() => new Map(series.map((item, index) => [item.id, index + 1])),
|
|
648
|
+
[series]
|
|
649
|
+
);
|
|
650
|
+
const yAxisLayer = useMemo(() => {
|
|
651
|
+
if (variant === "heatmap") {
|
|
652
|
+
return /* @__PURE__ */ jsx("g", { "aria-hidden": "true", children: visible.map((item, index) => /* @__PURE__ */ jsx(
|
|
653
|
+
"text",
|
|
654
|
+
{
|
|
655
|
+
"data-chart-row": "",
|
|
656
|
+
x: geometry.left - 8,
|
|
657
|
+
y: TOP + HEIGHT / visible.length * (index + 0.5),
|
|
658
|
+
textAnchor: "end",
|
|
659
|
+
dominantBaseline: "middle",
|
|
660
|
+
className: "fill-current font-mono text-oprt-base text-oprt-chart-axis",
|
|
661
|
+
children: seriesNumber.get(item.id)
|
|
662
|
+
},
|
|
663
|
+
item.id
|
|
664
|
+
)) });
|
|
665
|
+
}
|
|
666
|
+
return /* @__PURE__ */ jsx("g", { "data-chart-y-axis": "", "aria-hidden": "true", children: yAxis.ticks.map((value, index) => /* @__PURE__ */ jsxs("g", { children: [
|
|
667
|
+
/* @__PURE__ */ jsx(
|
|
668
|
+
"line",
|
|
669
|
+
{
|
|
670
|
+
x1: geometry.left,
|
|
671
|
+
x2: geometry.right,
|
|
672
|
+
y1: Math.round(geometry.y(value)) + 0.5,
|
|
673
|
+
y2: Math.round(geometry.y(value)) + 0.5,
|
|
674
|
+
className: "text-oprt-chart-grid",
|
|
675
|
+
stroke: "currentColor",
|
|
676
|
+
strokeWidth: "var(--oprt-chart-guide-width)",
|
|
677
|
+
strokeDasharray: value === 0 ? void 0 : "2 4",
|
|
678
|
+
strokeOpacity: value === 0 ? 1 : 0.65
|
|
679
|
+
}
|
|
680
|
+
),
|
|
681
|
+
/* @__PURE__ */ jsx(
|
|
682
|
+
"line",
|
|
683
|
+
{
|
|
684
|
+
x1: geometry.left - 4,
|
|
685
|
+
x2: geometry.left,
|
|
686
|
+
y1: Math.round(geometry.y(value)) + 0.5,
|
|
687
|
+
y2: Math.round(geometry.y(value)) + 0.5,
|
|
688
|
+
className: "text-oprt-chart-axis",
|
|
689
|
+
stroke: "currentColor",
|
|
690
|
+
strokeWidth: "var(--oprt-chart-guide-width)"
|
|
691
|
+
}
|
|
692
|
+
),
|
|
693
|
+
/* @__PURE__ */ jsxs(
|
|
694
|
+
"text",
|
|
695
|
+
{
|
|
696
|
+
x: geometry.left - 8,
|
|
697
|
+
y: geometry.y(value),
|
|
698
|
+
textAnchor: "end",
|
|
699
|
+
dominantBaseline: "middle",
|
|
700
|
+
className: "fill-current font-mono text-oprt-base text-oprt-chart-axis",
|
|
701
|
+
children: [
|
|
702
|
+
yLabels[index],
|
|
703
|
+
/* @__PURE__ */ jsx("title", { children: formatNumber(value) })
|
|
704
|
+
]
|
|
705
|
+
}
|
|
706
|
+
)
|
|
707
|
+
] }, index)) });
|
|
708
|
+
}, [variant, visible, yAxis, yLabels, geometry, seriesNumber, formatNumber]);
|
|
709
|
+
const xAxisLayer = useMemo(
|
|
710
|
+
() => /* @__PURE__ */ jsx("g", { "data-chart-x-axis": "", "aria-hidden": "true", children: xTickLabels.map((tick, index) => /* @__PURE__ */ jsxs(
|
|
711
|
+
"text",
|
|
712
|
+
{
|
|
713
|
+
x: tick.x,
|
|
714
|
+
y: BOTTOM + 22,
|
|
715
|
+
textAnchor: "middle",
|
|
716
|
+
className: "fill-current font-mono text-oprt-base text-oprt-chart-axis",
|
|
717
|
+
children: [
|
|
718
|
+
tick.shortText,
|
|
719
|
+
/* @__PURE__ */ jsx("title", { children: tick.text })
|
|
720
|
+
]
|
|
721
|
+
},
|
|
722
|
+
index
|
|
723
|
+
)) }),
|
|
724
|
+
[xTickLabels]
|
|
725
|
+
);
|
|
726
|
+
const xIndex = useMemo(
|
|
727
|
+
() => new Map(xValues.map((value, index) => [value, index])),
|
|
728
|
+
[xValues]
|
|
729
|
+
);
|
|
730
|
+
const activeIndex = inspection === null ? void 0 : xIndex.get(inspection);
|
|
731
|
+
const activeX = activeIndex === void 0 ? null : inspection;
|
|
732
|
+
const reviewCursor = inspectable && cursor != null && Number.isFinite(cursor) && cursor >= xDomain.min && cursor <= xDomain.max ? cursor : null;
|
|
733
|
+
useEffect(() => {
|
|
734
|
+
if (activeX === null || !inspectable) {
|
|
735
|
+
setInspection(null);
|
|
736
|
+
setPinned(false);
|
|
737
|
+
}
|
|
738
|
+
if (!inspectable || xValues.length === 0) setInspectionFocused(false);
|
|
739
|
+
}, [activeX, inspectable, xValues.length]);
|
|
740
|
+
const inspectIndex = activeIndex ?? Math.max(0, xValues.length - 1);
|
|
741
|
+
const formatPosition = useCallback(
|
|
742
|
+
(value) => valueWithUnit(formatX?.(value) ?? formatNumber(value), xUnit),
|
|
743
|
+
[formatX, formatNumber, xUnit]
|
|
744
|
+
);
|
|
745
|
+
function authoredLabel(value) {
|
|
746
|
+
let found;
|
|
747
|
+
for (const item of visible) {
|
|
748
|
+
const point = samplesAt.get(item)?.get(value);
|
|
749
|
+
if (!point) continue;
|
|
750
|
+
if (!point.label || found !== void 0 && point.label !== found) return void 0;
|
|
751
|
+
found = point.label;
|
|
752
|
+
}
|
|
753
|
+
return found;
|
|
754
|
+
}
|
|
755
|
+
const renderSamplePosition = (value) => authoredLabel(value) ?? /* @__PURE__ */ jsx(NumberText, { value: formatX?.(value) ?? formatNumber(value), unit: xUnit });
|
|
756
|
+
function describeSample(index) {
|
|
757
|
+
const value = xValues[index];
|
|
758
|
+
if (value === void 0) return "No sample";
|
|
759
|
+
return `${authoredLabel(value) ?? formatPosition(value)}. ${visible.map((item) => {
|
|
760
|
+
const point = samplesAt.get(item)?.get(value);
|
|
761
|
+
return `${item.label}: ${point && isFinitePoint(point) ? valueWithUnit(formatNumber(point.y), unit) : "Missing"}`;
|
|
762
|
+
}).join(". ")}`;
|
|
763
|
+
}
|
|
764
|
+
function inspectPointer(event) {
|
|
765
|
+
if (!inspectable || !xValues.length) return;
|
|
766
|
+
const bounds = event.currentTarget.getBoundingClientRect();
|
|
767
|
+
if (!bounds.width) return;
|
|
768
|
+
const px = (event.clientX - bounds.left) * plotWidth / bounds.width;
|
|
769
|
+
if (px < geometry.left || px > geometry.right) return;
|
|
770
|
+
setInspection(xValues[nearestIndex(xValues, px, geometry.x)] ?? null);
|
|
771
|
+
}
|
|
772
|
+
const changeRange = useCallback(
|
|
773
|
+
(next) => {
|
|
774
|
+
if (disabled) return;
|
|
775
|
+
if (!controlledRange) setLocalRange(next);
|
|
776
|
+
onRangeChange?.(next);
|
|
777
|
+
setInspection(null);
|
|
778
|
+
setPinned(false);
|
|
779
|
+
},
|
|
780
|
+
[disabled, controlledRange, onRangeChange]
|
|
781
|
+
);
|
|
782
|
+
function toggle(item) {
|
|
783
|
+
const next = new Set(hidden);
|
|
784
|
+
if (next.has(item.id)) next.delete(item.id);
|
|
785
|
+
else next.add(item.id);
|
|
786
|
+
const ids2 = [...next];
|
|
787
|
+
if (hiddenSeries === void 0) setLocalHidden(ids2);
|
|
788
|
+
onHiddenSeriesChange?.(ids2);
|
|
789
|
+
}
|
|
790
|
+
const firstX = xValues[0];
|
|
791
|
+
const lastX = xValues[xValues.length - 1];
|
|
792
|
+
const windowEnds = empty ? null : variant === "heatmap" ? firstX === void 0 || lastX === void 0 ? null : [firstX, lastX] : [xDomain.min, xDomain.max];
|
|
793
|
+
const renderPosition = useCallback(
|
|
794
|
+
(value) => histogramView ? /* @__PURE__ */ jsx(NumberText, { value: formatNumber(value), unit }) : /* @__PURE__ */ jsx(NumberText, { value: formatX?.(value) ?? formatNumber(value), unit: xUnit }),
|
|
795
|
+
[histogramView, unit, formatX, formatNumber, xUnit]
|
|
796
|
+
);
|
|
797
|
+
const { x, y } = geometry;
|
|
798
|
+
return /* @__PURE__ */ jsxs(
|
|
799
|
+
"div",
|
|
800
|
+
{
|
|
801
|
+
...props,
|
|
802
|
+
ref,
|
|
803
|
+
"data-oprt-chart": variant,
|
|
804
|
+
className: clsx(chartStyles({ variant }), className),
|
|
805
|
+
children: [
|
|
806
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2u", children: [
|
|
807
|
+
/* @__PURE__ */ jsx(
|
|
808
|
+
"h3",
|
|
809
|
+
{
|
|
810
|
+
ref: labelRef,
|
|
811
|
+
id: ids.label,
|
|
812
|
+
className: "m-0 min-w-0 flex-1 font-semibold text-oprt-base uppercase tracking-oprt-caps oprt-wrap-anywhere",
|
|
813
|
+
children: label
|
|
814
|
+
}
|
|
815
|
+
),
|
|
816
|
+
/* @__PURE__ */ jsx(
|
|
817
|
+
Button,
|
|
818
|
+
{
|
|
819
|
+
variant: "tertiary",
|
|
820
|
+
size: "sm",
|
|
821
|
+
"aria-expanded": showData,
|
|
822
|
+
"aria-controls": showData ? ids.data : void 0,
|
|
823
|
+
onClick: () => setShowData(!showData),
|
|
824
|
+
children: "View data"
|
|
825
|
+
}
|
|
826
|
+
)
|
|
827
|
+
] }),
|
|
828
|
+
description && /* @__PURE__ */ jsx("p", { className: "m-0 mt-1u text-oprt-base text-oprt-text-dim oprt-wrap-anywhere", children: description }),
|
|
829
|
+
/* @__PURE__ */ jsxs("p", { id: ids.description, className: "sr-only", children: [
|
|
830
|
+
description ? `${description} ` : "",
|
|
831
|
+
variant,
|
|
832
|
+
" chart. ",
|
|
833
|
+
formatNumber(visible.length),
|
|
834
|
+
" visible series. ",
|
|
835
|
+
formatNumber(missing),
|
|
836
|
+
" ",
|
|
837
|
+
"missing samples. ",
|
|
838
|
+
empty ?? "Use View data to inspect values."
|
|
839
|
+
] }),
|
|
840
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2u flex justify-between gap-2u text-oprt-base uppercase tracking-oprt-caps text-oprt-text-dim", children: [
|
|
841
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 oprt-wrap-anywhere", children: variant === "heatmap" ? "Series" : yHeading }),
|
|
842
|
+
variant === "heatmap" && /* @__PURE__ */ jsx("span", { className: "min-w-0 oprt-wrap-anywhere", children: yHeading })
|
|
843
|
+
] }),
|
|
844
|
+
/* @__PURE__ */ jsxs(
|
|
845
|
+
"div",
|
|
846
|
+
{
|
|
847
|
+
ref: plotRef,
|
|
848
|
+
className: "relative min-w-0 w-full border border-transparent focus-within:border-oprt-border-focus focus-within:shadow-oprt-focus",
|
|
849
|
+
onKeyDownCapture: (event) => {
|
|
850
|
+
if (event.key !== "Escape" || activeX === null && !pinned) return;
|
|
851
|
+
event.preventDefault();
|
|
852
|
+
event.stopPropagation();
|
|
853
|
+
setInspection(null);
|
|
854
|
+
setPinned(false);
|
|
855
|
+
},
|
|
856
|
+
children: [
|
|
857
|
+
inspectable && xValues.length > 0 && /* @__PURE__ */ jsx(
|
|
858
|
+
ChartInspection,
|
|
859
|
+
{
|
|
860
|
+
label,
|
|
861
|
+
index: inspectIndex,
|
|
862
|
+
count: xValues.length,
|
|
863
|
+
describe: describeSample,
|
|
864
|
+
inputRef: inspectionRef,
|
|
865
|
+
onChange: (index) => {
|
|
866
|
+
setInspection(xValues[index] ?? null);
|
|
867
|
+
setPinned(true);
|
|
868
|
+
},
|
|
869
|
+
onFocus: () => {
|
|
870
|
+
setInspectionFocused(true);
|
|
871
|
+
setInspection(xValues[inspectIndex] ?? null);
|
|
872
|
+
setPinned(true);
|
|
873
|
+
},
|
|
874
|
+
onBlur: () => setInspectionFocused(false)
|
|
875
|
+
}
|
|
876
|
+
),
|
|
877
|
+
empty ? /* @__PURE__ */ jsx("p", { className: "m-0 py-6u text-oprt-base text-oprt-text-dim", role: "status", children: empty }) : /* @__PURE__ */ jsxs(
|
|
878
|
+
"svg",
|
|
879
|
+
{
|
|
880
|
+
role: "img",
|
|
881
|
+
"aria-labelledby": ids.label,
|
|
882
|
+
"aria-describedby": ids.description,
|
|
883
|
+
viewBox: `0 0 ${plotWidth} ${SVG_HEIGHT}`,
|
|
884
|
+
height: SVG_HEIGHT,
|
|
885
|
+
preserveAspectRatio: "xMinYMin meet",
|
|
886
|
+
className: "block w-full overflow-hidden",
|
|
887
|
+
onPointerMove: (event) => {
|
|
888
|
+
if (!pinned && !inspectionFocused) inspectPointer(event);
|
|
889
|
+
},
|
|
890
|
+
onPointerLeave: () => {
|
|
891
|
+
if (!pinned && !inspectionFocused) setInspection(null);
|
|
892
|
+
},
|
|
893
|
+
onPointerDown: (event) => {
|
|
894
|
+
if (inspectable && event.button === 0) {
|
|
895
|
+
inspectionRef.current?.focus({ preventScroll: true });
|
|
896
|
+
inspectPointer(event);
|
|
897
|
+
setPinned(true);
|
|
898
|
+
}
|
|
899
|
+
},
|
|
900
|
+
children: [
|
|
901
|
+
fill === "hatched" && (variant === "area" || variant === "bar") && /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("pattern", { id: hatchId, width: 8, height: 8, patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ jsx(
|
|
902
|
+
"path",
|
|
903
|
+
{
|
|
904
|
+
d: "M-2 2 L2 -2 M0 8 L8 0 M6 10 L10 6",
|
|
905
|
+
stroke: "var(--oprt-text-dim)",
|
|
906
|
+
strokeWidth: "var(--oprt-chart-guide-width)",
|
|
907
|
+
strokeOpacity: "var(--oprt-chart-bar-opacity)"
|
|
908
|
+
}
|
|
909
|
+
) }) }),
|
|
910
|
+
yAxisLayer,
|
|
911
|
+
visible.map((item, index) => /* @__PURE__ */ jsx(
|
|
912
|
+
"g",
|
|
913
|
+
{
|
|
914
|
+
className: "transition-opacity duration-oprt-normal motion-reduce:transition-none",
|
|
915
|
+
opacity: emphasized && emphasized !== item.id ? 0.35 : 1,
|
|
916
|
+
"aria-hidden": "true",
|
|
917
|
+
children: marksLayer[index]
|
|
918
|
+
},
|
|
919
|
+
item.id
|
|
920
|
+
)),
|
|
921
|
+
thresholdValue !== void 0 && /* @__PURE__ */ jsx(
|
|
922
|
+
"line",
|
|
923
|
+
{
|
|
924
|
+
"data-chart-threshold": "",
|
|
925
|
+
x1: geometry.left,
|
|
926
|
+
x2: geometry.right,
|
|
927
|
+
y1: y(thresholdValue),
|
|
928
|
+
y2: y(thresholdValue),
|
|
929
|
+
className: "text-oprt-chart-threshold",
|
|
930
|
+
stroke: "currentColor",
|
|
931
|
+
strokeWidth: "var(--oprt-chart-guide-width)",
|
|
932
|
+
strokeDasharray: "6 4"
|
|
933
|
+
}
|
|
934
|
+
),
|
|
935
|
+
reviewCursor !== null && /* @__PURE__ */ jsx(
|
|
936
|
+
"line",
|
|
937
|
+
{
|
|
938
|
+
"data-chart-cursor": "",
|
|
939
|
+
"aria-hidden": "true",
|
|
940
|
+
pointerEvents: "none",
|
|
941
|
+
x1: x(reviewCursor),
|
|
942
|
+
x2: x(reviewCursor),
|
|
943
|
+
y1: TOP,
|
|
944
|
+
y2: BOTTOM,
|
|
945
|
+
className: "text-oprt-accent",
|
|
946
|
+
stroke: "currentColor",
|
|
947
|
+
strokeWidth: "var(--oprt-chart-stroke-width)"
|
|
948
|
+
}
|
|
949
|
+
),
|
|
950
|
+
inspectable && activeX !== null && /* @__PURE__ */ jsxs("g", { "data-chart-crosshair": "", "aria-hidden": "true", pointerEvents: "none", children: [
|
|
951
|
+
/* @__PURE__ */ jsx(
|
|
952
|
+
"line",
|
|
953
|
+
{
|
|
954
|
+
x1: x(activeX),
|
|
955
|
+
x2: x(activeX),
|
|
956
|
+
y1: TOP,
|
|
957
|
+
y2: BOTTOM,
|
|
958
|
+
stroke: "var(--oprt-text-dim)",
|
|
959
|
+
strokeWidth: "var(--oprt-chart-guide-width)",
|
|
960
|
+
strokeDasharray: "3 3"
|
|
961
|
+
}
|
|
962
|
+
),
|
|
963
|
+
visible.map((item, index) => {
|
|
964
|
+
const point = samplesAt.get(item)?.get(activeX);
|
|
965
|
+
if (!point || !isFinitePoint(point)) return null;
|
|
966
|
+
const pointX = variant === "bar" ? x(activeX) - geometry.groupWidth / 2 + geometry.groupWidth / visible.length * (index + 0.45) : x(activeX);
|
|
967
|
+
return /* @__PURE__ */ jsx("g", { className: colorClass(item), children: /* @__PURE__ */ jsx(
|
|
968
|
+
Marker,
|
|
969
|
+
{
|
|
970
|
+
identity: seriesIdentity(item.id),
|
|
971
|
+
x: pointX,
|
|
972
|
+
y: y(point.y),
|
|
973
|
+
size: 4,
|
|
974
|
+
solid: true
|
|
975
|
+
}
|
|
976
|
+
) }, item.id);
|
|
977
|
+
})
|
|
978
|
+
] }),
|
|
979
|
+
xAxisLayer
|
|
980
|
+
]
|
|
981
|
+
}
|
|
982
|
+
)
|
|
983
|
+
]
|
|
984
|
+
}
|
|
985
|
+
),
|
|
986
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline justify-between gap-x-2u", children: [
|
|
987
|
+
windowEnds && /* @__PURE__ */ jsxs(
|
|
988
|
+
"p",
|
|
989
|
+
{
|
|
990
|
+
"data-chart-window": "",
|
|
991
|
+
className: "m-0 min-w-0 font-mono text-oprt-base text-oprt-text-dim oprt-wrap-anywhere",
|
|
992
|
+
children: [
|
|
993
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block max-w-full", children: renderPosition(windowEnds[0]) }),
|
|
994
|
+
" to ",
|
|
995
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block max-w-full", children: renderPosition(windowEnds[1]) })
|
|
996
|
+
]
|
|
997
|
+
}
|
|
998
|
+
),
|
|
999
|
+
/* @__PURE__ */ jsx("p", { className: "m-0 ml-auto min-w-0 text-right text-oprt-base uppercase tracking-oprt-caps text-oprt-text-dim oprt-wrap-anywhere", children: xHeading })
|
|
1000
|
+
] }),
|
|
1001
|
+
reviewCursor !== null && /* @__PURE__ */ jsxs("p", { className: "m-0 text-oprt-base text-oprt-text-dim oprt-wrap-anywhere", children: [
|
|
1002
|
+
cursorLabel,
|
|
1003
|
+
": ",
|
|
1004
|
+
renderSamplePosition(reviewCursor)
|
|
1005
|
+
] }),
|
|
1006
|
+
inspectable && /* @__PURE__ */ jsxs(
|
|
1007
|
+
"div",
|
|
1008
|
+
{
|
|
1009
|
+
role: "region",
|
|
1010
|
+
"aria-label": `${label} readout`,
|
|
1011
|
+
className: "mt-2u border-y border-oprt-border bg-oprt-surface-2 px-2u py-1u",
|
|
1012
|
+
children: [
|
|
1013
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-h-3u flex-wrap items-center justify-between gap-1u", children: [
|
|
1014
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 font-mono text-oprt-base oprt-wrap-anywhere", children: activeX !== null ? renderSamplePosition(activeX) : "Hover or focus the plot to inspect" }),
|
|
1015
|
+
/* @__PURE__ */ jsx(FocusableWhenUnavailableProvider, { value: true, children: /* @__PURE__ */ jsx(
|
|
1016
|
+
Button,
|
|
1017
|
+
{
|
|
1018
|
+
variant: "ghost",
|
|
1019
|
+
size: "sm",
|
|
1020
|
+
disabled: activeX === null,
|
|
1021
|
+
onClick: () => {
|
|
1022
|
+
setInspection(null);
|
|
1023
|
+
setPinned(false);
|
|
1024
|
+
},
|
|
1025
|
+
children: "Clear inspection"
|
|
1026
|
+
}
|
|
1027
|
+
) })
|
|
1028
|
+
] }),
|
|
1029
|
+
/* @__PURE__ */ jsx("dl", { className: "m-0 flex flex-wrap gap-x-3u gap-y-1u pb-1u", children: visible.map((item) => {
|
|
1030
|
+
const point = activeX === null ? void 0 : samplesAt.get(item)?.get(activeX);
|
|
1031
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1u", children: [
|
|
1032
|
+
/* @__PURE__ */ jsx(
|
|
1033
|
+
"svg",
|
|
1034
|
+
{
|
|
1035
|
+
width: "16",
|
|
1036
|
+
height: "16",
|
|
1037
|
+
viewBox: "0 0 16 16",
|
|
1038
|
+
"aria-hidden": "true",
|
|
1039
|
+
className: clsx("shrink-0", colorClass(item)),
|
|
1040
|
+
children: /* @__PURE__ */ jsx(Marker, { identity: seriesIdentity(item.id), x: 8, y: 8, size: 3 })
|
|
1041
|
+
}
|
|
1042
|
+
),
|
|
1043
|
+
/* @__PURE__ */ jsx("dt", { className: "min-w-0 text-oprt-base text-oprt-text-dim oprt-wrap-anywhere", children: item.label }),
|
|
1044
|
+
/* @__PURE__ */ jsx("dd", { className: "m-0 font-mono text-oprt-base", children: activeX === null ? "\u2014" : point && isFinitePoint(point) ? /* @__PURE__ */ jsx(NumberText, { value: formatNumber(point.y), unit }) : "Missing" })
|
|
1045
|
+
] }, item.id);
|
|
1046
|
+
}) })
|
|
1047
|
+
]
|
|
1048
|
+
}
|
|
1049
|
+
),
|
|
1050
|
+
thresholdValue !== void 0 && threshold && /* @__PURE__ */ jsxs("p", { className: "m-0 mt-1u font-mono text-oprt-base text-oprt-chart-threshold oprt-wrap-anywhere", children: [
|
|
1051
|
+
threshold.label,
|
|
1052
|
+
":",
|
|
1053
|
+
" ",
|
|
1054
|
+
/* @__PURE__ */ jsx(NumberText, { value: formatNumber(thresholdValue), unit: distribution ? void 0 : unit })
|
|
1055
|
+
] }),
|
|
1056
|
+
missing > 0 && /* @__PURE__ */ jsx("p", { className: "m-0 mt-1u text-oprt-base text-oprt-text-dim", children: distribution ? `${formatNumber(missing)} missing samples excluded from bins.` : `${formatNumber(missing)} missing samples shown as gaps.` }),
|
|
1057
|
+
variant === "heatmap" && finite.length > 0 && /* @__PURE__ */ jsxs(
|
|
1058
|
+
"div",
|
|
1059
|
+
{
|
|
1060
|
+
className: "mt-2u flex flex-wrap items-center gap-1u text-oprt-base",
|
|
1061
|
+
"aria-label": "Value scale",
|
|
1062
|
+
children: [
|
|
1063
|
+
/* @__PURE__ */ jsx(
|
|
1064
|
+
NumberText,
|
|
1065
|
+
{
|
|
1066
|
+
value: formatNumber(heatDomain.min),
|
|
1067
|
+
unit,
|
|
1068
|
+
className: "min-w-0 font-mono"
|
|
1069
|
+
}
|
|
1070
|
+
),
|
|
1071
|
+
/* @__PURE__ */ jsx("span", { className: "flex shrink-0 gap-1u", "aria-hidden": "true", children: sequentialClasses.map((color) => /* @__PURE__ */ jsx("span", { className: clsx("h-1u w-3u bg-current", color, KEEP_COLORS) }, color)) }),
|
|
1072
|
+
/* @__PURE__ */ jsx(
|
|
1073
|
+
NumberText,
|
|
1074
|
+
{
|
|
1075
|
+
value: formatNumber(heatDomain.max),
|
|
1076
|
+
unit,
|
|
1077
|
+
className: "min-w-0 font-mono"
|
|
1078
|
+
}
|
|
1079
|
+
)
|
|
1080
|
+
]
|
|
1081
|
+
}
|
|
1082
|
+
),
|
|
1083
|
+
series.length > 0 && /* @__PURE__ */ jsx(
|
|
1084
|
+
"div",
|
|
1085
|
+
{
|
|
1086
|
+
className: "mt-2u flex flex-wrap gap-1u",
|
|
1087
|
+
role: "group",
|
|
1088
|
+
"aria-label": `${label} series visibility`,
|
|
1089
|
+
children: series.map((item) => /* @__PURE__ */ jsxs(
|
|
1090
|
+
Button,
|
|
1091
|
+
{
|
|
1092
|
+
variant: "ghost",
|
|
1093
|
+
size: "sm",
|
|
1094
|
+
className: "min-w-0 max-w-full oprt-wrap-anywhere",
|
|
1095
|
+
"aria-label": variant === "heatmap" ? `${seriesNumber.get(item.id)} ${item.label}` : item.label,
|
|
1096
|
+
"aria-pressed": !hidden.has(item.id),
|
|
1097
|
+
disabled,
|
|
1098
|
+
onClick: () => {
|
|
1099
|
+
toggle(item);
|
|
1100
|
+
setEmphasized(null);
|
|
1101
|
+
},
|
|
1102
|
+
onPointerEnter: () => {
|
|
1103
|
+
if (!hidden.has(item.id)) setEmphasized(item.id);
|
|
1104
|
+
},
|
|
1105
|
+
onPointerLeave: () => setEmphasized(null),
|
|
1106
|
+
onFocus: () => {
|
|
1107
|
+
if (!hidden.has(item.id)) setEmphasized(item.id);
|
|
1108
|
+
},
|
|
1109
|
+
onBlur: () => setEmphasized(null),
|
|
1110
|
+
children: [
|
|
1111
|
+
variant !== "heatmap" && /* @__PURE__ */ jsxs(
|
|
1112
|
+
"svg",
|
|
1113
|
+
{
|
|
1114
|
+
"aria-hidden": "true",
|
|
1115
|
+
width: "28",
|
|
1116
|
+
height: "16",
|
|
1117
|
+
viewBox: "0 0 28 16",
|
|
1118
|
+
className: clsx("mr-1u shrink-0", colorClass(item)),
|
|
1119
|
+
children: [
|
|
1120
|
+
(variant === "line" || variant === "area") && /* @__PURE__ */ jsx(
|
|
1121
|
+
"path",
|
|
1122
|
+
{
|
|
1123
|
+
d: "M1 8 H27",
|
|
1124
|
+
fill: "none",
|
|
1125
|
+
stroke: "currentColor",
|
|
1126
|
+
strokeWidth: "var(--oprt-chart-stroke-width)",
|
|
1127
|
+
strokeDasharray: dashPatterns[seriesIdentity(item.id)]
|
|
1128
|
+
}
|
|
1129
|
+
),
|
|
1130
|
+
(variant === "bar" || variant === "histogram") && /* @__PURE__ */ jsx(
|
|
1131
|
+
"rect",
|
|
1132
|
+
{
|
|
1133
|
+
x: 8,
|
|
1134
|
+
y: 5,
|
|
1135
|
+
width: 12,
|
|
1136
|
+
height: 9,
|
|
1137
|
+
fill: "currentColor",
|
|
1138
|
+
fillOpacity: "var(--oprt-chart-bar-opacity)",
|
|
1139
|
+
stroke: "currentColor",
|
|
1140
|
+
strokeWidth: "var(--oprt-chart-stroke-width)"
|
|
1141
|
+
}
|
|
1142
|
+
),
|
|
1143
|
+
/* @__PURE__ */ jsx(
|
|
1144
|
+
Marker,
|
|
1145
|
+
{
|
|
1146
|
+
identity: seriesIdentity(item.id),
|
|
1147
|
+
x: 14,
|
|
1148
|
+
y: variant === "bar" || variant === "histogram" ? 5 : 8,
|
|
1149
|
+
size: 2.5,
|
|
1150
|
+
solid: variant === "line" || variant === "area"
|
|
1151
|
+
}
|
|
1152
|
+
)
|
|
1153
|
+
]
|
|
1154
|
+
}
|
|
1155
|
+
),
|
|
1156
|
+
variant === "heatmap" && /* @__PURE__ */ jsx("span", { className: "mr-1u font-mono", children: seriesNumber.get(item.id) }),
|
|
1157
|
+
item.label,
|
|
1158
|
+
hidden.has(item.id) && /* @__PURE__ */ jsx("span", { className: "ml-1u text-oprt-text-dim", children: "Hidden" })
|
|
1159
|
+
]
|
|
1160
|
+
},
|
|
1161
|
+
item.id
|
|
1162
|
+
))
|
|
1163
|
+
}
|
|
1164
|
+
),
|
|
1165
|
+
rangeSelection && inspectable && fullDomain.min < fullDomain.max && anyFiniteX && /* @__PURE__ */ jsx(
|
|
1166
|
+
ChartRangeSelector,
|
|
1167
|
+
{
|
|
1168
|
+
label,
|
|
1169
|
+
series: fullVisible,
|
|
1170
|
+
domain: fullDomain,
|
|
1171
|
+
range: view,
|
|
1172
|
+
format: formatPosition,
|
|
1173
|
+
renderPosition,
|
|
1174
|
+
disabled,
|
|
1175
|
+
onChange: changeRange
|
|
1176
|
+
}
|
|
1177
|
+
),
|
|
1178
|
+
showData && /* @__PURE__ */ jsx("div", { className: "mt-2u", children: /* @__PURE__ */ jsx(
|
|
1179
|
+
DataView,
|
|
1180
|
+
{
|
|
1181
|
+
id: ids.data,
|
|
1182
|
+
label,
|
|
1183
|
+
series: distribution ? visible : series,
|
|
1184
|
+
hidden,
|
|
1185
|
+
distribution,
|
|
1186
|
+
xHeading,
|
|
1187
|
+
yHeading,
|
|
1188
|
+
formatX,
|
|
1189
|
+
formatNumber,
|
|
1190
|
+
separator
|
|
1191
|
+
}
|
|
1192
|
+
) })
|
|
1193
|
+
]
|
|
1194
|
+
}
|
|
1195
|
+
);
|
|
1196
|
+
});
|
|
1197
|
+
Chart.displayName = "Chart";
|
|
1198
|
+
export {
|
|
1199
|
+
Chart
|
|
1200
|
+
};
|