@altertable/data-app 0.66.0 → 0.68.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/CONTRIBUTING.md +22 -16
- package/dist/chunks/{contract-19ckn3n7.js → index-23dktynd.js} +8 -6
- package/dist/chunks/index-23dktynd.js.map +10 -0
- package/dist/chunks/{contract-rfbakka4.js → index-ag0cgnvq.js} +169 -35
- package/dist/chunks/index-ag0cgnvq.js.map +13 -0
- package/dist/chunks/index-dq7b35fn.js +307 -0
- package/dist/chunks/index-dq7b35fn.js.map +13 -0
- package/dist/chunks/{contract-e11m4y7f.js → index-ev2b5aaf.js} +11 -6
- package/dist/chunks/{contract-e11m4y7f.js.map → index-ev2b5aaf.js.map} +3 -3
- package/dist/chunks/{contract-awa2d5b1.js → index-f5cnbrjz.js} +19 -2
- package/dist/chunks/{contract-awa2d5b1.js.map → index-f5cnbrjz.js.map} +4 -3
- package/dist/chunks/{contract-havnjmdr.js → index-gvers8jw.js} +2429 -1110
- package/dist/chunks/index-gvers8jw.js.map +109 -0
- package/dist/chunks/{contract-13j5zb3c.js → index-ktmvs916.js} +8 -7
- package/dist/chunks/index-ktmvs916.js.map +13 -0
- package/dist/chunks/index-mt93ff2b.js +28 -0
- package/dist/chunks/index-mt93ff2b.js.map +10 -0
- package/dist/chunks/{contract-tf8c3qpv.js → index-qd5xgx6y.js} +46 -5
- package/dist/chunks/index-qd5xgx6y.js.map +13 -0
- package/dist/chunks/{contract-pk603qj7.js → index-x69p7cv7.js} +12 -9
- package/dist/chunks/index-x69p7cv7.js.map +10 -0
- package/dist/client/index.js +6 -6
- package/dist/core/appearance.js +1 -1
- package/dist/core/config.js +7 -3
- package/dist/core/config.js.map +1 -1
- package/dist/core/contract.js +14 -6
- package/dist/core/contract.js.map +1 -1
- package/dist/core/format.js +1 -1
- package/dist/embed/index.js +12 -10
- package/dist/embed/index.js.map +4 -4
- package/dist/index.js +12 -0
- package/dist/index.js.map +9 -0
- package/dist/local.js +20 -30
- package/dist/local.js.map +7 -8
- package/dist/react/embed/index.js +1 -1
- package/dist/react/embed/index.js.map +2 -2
- package/dist/react/index.js +79 -29
- package/dist/react/index.js.map +5 -6
- package/dist/react/ui/index.js +1702 -177
- package/dist/react/ui/index.js.map +20 -9
- package/dist/server.js +13 -26
- package/dist/server.js.map +6 -7
- package/dist/types/client/annotations.d.ts +4 -0
- package/dist/types/client/data-client.d.ts +1 -2
- package/dist/types/client/iframe.d.ts +4 -2
- package/dist/types/core/annotations.d.ts +2 -0
- package/dist/types/core/config.d.ts +48 -4
- package/dist/types/core/contract.d.ts +20 -12
- package/dist/types/core/dimension.d.ts +6 -4
- package/dist/types/core/filters.d.ts +44 -0
- package/dist/types/core/messages.d.ts +2 -0
- package/dist/types/core/operation-types.d.ts +5 -2
- package/dist/types/core/operation.d.ts +3 -4
- package/dist/types/core/queries.d.ts +16 -0
- package/dist/types/core/uuid.d.ts +2 -0
- package/dist/types/core/variables.d.ts +32 -13
- package/dist/types/embed/runtime-html.d.ts +2 -0
- package/dist/types/embed/source.d.ts +2 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/react/annotations/AnnotationBar.d.ts +7 -1
- package/dist/types/react/annotations/AnnotationEditor.d.ts +2 -1
- package/dist/types/react/annotations/useDataAppAnnotations.d.ts +3 -0
- package/dist/types/react/app-context.d.ts +3 -0
- package/dist/types/react/index.d.ts +3 -3
- package/dist/types/react/mount.d.ts +6 -4
- package/dist/types/react/style-contract.d.ts +34 -2
- package/dist/types/react/ui/Button.d.ts +3 -3
- package/dist/types/react/ui/ChartLegend.d.ts +31 -0
- package/dist/types/react/ui/Checkbox.d.ts +15 -4
- package/dist/types/react/ui/CheckboxGroup.d.ts +7 -0
- package/dist/types/react/ui/{Combobox.d.ts → ChoicePicker.d.ts} +10 -15
- package/dist/types/react/ui/ComposedChart.d.ts +41 -0
- package/dist/types/react/ui/ComposedChartLegend.d.ts +8 -0
- package/dist/types/react/ui/DataAppFrame.d.ts +1 -2
- package/dist/types/react/ui/DimensionPicker.d.ts +1 -1
- package/dist/types/react/ui/FilterActions.d.ts +10 -0
- package/dist/types/react/ui/FilterBar.d.ts +3 -0
- package/dist/types/react/ui/FunnelChart.d.ts +20 -0
- package/dist/types/react/ui/GettingStarted.d.ts +2 -4
- package/dist/types/react/ui/JourneyChart.d.ts +10 -0
- package/dist/types/react/ui/Menu.d.ts +12 -0
- package/dist/types/react/ui/NumberField.d.ts +27 -0
- package/dist/types/react/ui/NumberFilterPicker.d.ts +9 -0
- package/dist/types/react/ui/PieChart.d.ts +5 -2
- package/dist/types/react/ui/RadioGroup.d.ts +18 -0
- package/dist/types/react/ui/RetentionChart.d.ts +27 -0
- package/dist/types/react/ui/SearchInput.d.ts +2 -2
- package/dist/types/react/ui/Select.d.ts +14 -0
- package/dist/types/react/ui/SelectionMark.d.ts +2 -1
- package/dist/types/react/ui/TooltipSurface.d.ts +3 -0
- package/dist/types/react/ui/chart-data.d.ts +10 -1
- package/dist/types/react/ui/chart-primitives.d.ts +22 -0
- package/dist/types/react/ui/chartColor.d.ts +1 -0
- package/dist/types/react/ui/icons.d.ts +1 -0
- package/dist/types/react/ui/index.d.ts +33 -2
- package/dist/types/react/ui/journey-flow.d.ts +49 -0
- package/dist/types/react/ui/keyboard.d.ts +6 -0
- package/dist/types/react/ui/shortcuts.d.ts +7 -0
- package/dist/types/react/ui/useAppAppearance.d.ts +1 -1
- package/dist/types/react/ui/useSvgId.d.ts +2 -0
- package/dist/types/react/ui/variables.d.ts +4 -2
- package/dist/types/react/view-controls.d.ts +3 -1
- package/dist/types/server/handler.d.ts +4 -4
- package/dist/worker.js +21 -738
- package/docs/app-authoring.md +15 -1
- package/docs/client.md +8 -8
- package/docs/contract.md +43 -20
- package/docs/data-context.md +8 -6
- package/docs/embed.md +10 -2
- package/docs/formatting-and-appearance.md +23 -7
- package/docs/hosted-apps.md +9 -2
- package/docs/layout.md +1 -1
- package/docs/react-embed.md +3 -2
- package/docs/react.md +4 -3
- package/docs/server-bun.md +1 -1
- package/docs/server.md +4 -4
- package/docs/styling.md +10 -3
- package/docs/ui.md +176 -3
- package/docs/variables.md +80 -7
- package/docs/views.md +4 -8
- package/docs/widgets.md +19 -13
- package/docs/worker.md +2 -1
- package/examples/starter-data-app/index.tsx +34 -32
- package/package.json +16 -11
- package/dist/chunks/contract-13j5zb3c.js.map +0 -13
- package/dist/chunks/contract-19ckn3n7.js.map +0 -10
- package/dist/chunks/contract-h8a6f559.js +0 -286
- package/dist/chunks/contract-h8a6f559.js.map +0 -12
- package/dist/chunks/contract-havnjmdr.js.map +0 -99
- package/dist/chunks/contract-pk603qj7.js.map +0 -10
- package/dist/chunks/contract-rfbakka4.js.map +0 -13
- package/dist/chunks/contract-tf8c3qpv.js.map +0 -11
- package/dist/chunks/contract-wz59z8pq.js +0 -8
- package/dist/chunks/contract-wz59z8pq.js.map +0 -10
- /package/dist/chunks/{contract-m6n8ctfc.js → index-m6n8ctfc.js} +0 -0
- /package/dist/chunks/{contract-m6n8ctfc.js.map → index-m6n8ctfc.js.map} +0 -0
- /package/dist/chunks/{contract-mev09s5v.js → index-mev09s5v.js} +0 -0
- /package/dist/chunks/{contract-mev09s5v.js.map → index-mev09s5v.js.map} +0 -0
package/dist/react/ui/index.js
CHANGED
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
AppIcon2,
|
|
5
5
|
classNames,
|
|
6
6
|
Button2,
|
|
7
|
+
PressButton,
|
|
8
|
+
TooltipSurface,
|
|
7
9
|
TooltipProvider2,
|
|
8
10
|
Tooltip2,
|
|
9
11
|
GradientScroll2,
|
|
@@ -17,18 +19,34 @@ import {
|
|
|
17
19
|
AboutData2,
|
|
18
20
|
DataAppProvider2,
|
|
19
21
|
useDeclaredResult,
|
|
22
|
+
validateChoiceOptions,
|
|
20
23
|
useAppVariables2,
|
|
24
|
+
Select2,
|
|
25
|
+
FilterActions2,
|
|
26
|
+
NumberField2,
|
|
27
|
+
NumberRangeField2,
|
|
28
|
+
NumberFilterPicker2,
|
|
21
29
|
SearchField2,
|
|
22
30
|
Skeleton2,
|
|
23
31
|
SearchMatch2,
|
|
24
32
|
searchItems2,
|
|
25
|
-
|
|
33
|
+
ChoicePicker2,
|
|
34
|
+
CheckboxGroupContext,
|
|
26
35
|
Checkbox2,
|
|
27
36
|
DateRangePicker2,
|
|
37
|
+
FilterBar2,
|
|
28
38
|
DimensionPicker2,
|
|
29
39
|
createDataHooks2,
|
|
40
|
+
isPlainKeyEvent2,
|
|
41
|
+
MenuTrigger2,
|
|
42
|
+
MenuSection2,
|
|
43
|
+
MenuPopover2,
|
|
44
|
+
Menu2,
|
|
45
|
+
MenuItem2,
|
|
46
|
+
MenuSeparator2,
|
|
30
47
|
LiveControl2,
|
|
31
48
|
PresentStory2,
|
|
49
|
+
VariableBar2,
|
|
32
50
|
DataAppFrame,
|
|
33
51
|
Grid2,
|
|
34
52
|
DataWidget2,
|
|
@@ -42,19 +60,24 @@ import {
|
|
|
42
60
|
TableWidget2,
|
|
43
61
|
TextWidget2,
|
|
44
62
|
DataValue2,
|
|
63
|
+
chartBarFill,
|
|
45
64
|
createDataContext2
|
|
46
|
-
} from "../../chunks/
|
|
47
|
-
import"../../chunks/contract-wz59z8pq.js";
|
|
48
|
-
import"../../chunks/contract-mev09s5v.js";
|
|
65
|
+
} from "../../chunks/index-gvers8jw.js";
|
|
49
66
|
import {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
67
|
+
formatNumber2,
|
|
68
|
+
formatPercent2
|
|
69
|
+
} from "../../chunks/index-mev09s5v.js";
|
|
70
|
+
import"../../chunks/index-ag0cgnvq.js";
|
|
71
|
+
import"../../chunks/index-mt93ff2b.js";
|
|
72
|
+
import"../../chunks/index-m6n8ctfc.js";
|
|
53
73
|
import {
|
|
54
74
|
createDataClient2
|
|
55
|
-
} from "../../chunks/
|
|
56
|
-
import"../../chunks/
|
|
57
|
-
import"../../chunks/
|
|
75
|
+
} from "../../chunks/index-23dktynd.js";
|
|
76
|
+
import"../../chunks/index-x69p7cv7.js";
|
|
77
|
+
import"../../chunks/index-ev2b5aaf.js";
|
|
78
|
+
import {
|
|
79
|
+
invariant
|
|
80
|
+
} from "../../chunks/index-qd5xgx6y.js";
|
|
58
81
|
|
|
59
82
|
// src/react/shellStyles.ts
|
|
60
83
|
function injectDataAppShellStyles(options = {}) {
|
|
@@ -64,6 +87,7 @@ function injectDataAppShellStyles(options = {}) {
|
|
|
64
87
|
css: `/* src/react/tokens.css */
|
|
65
88
|
@layer atbl.tokens {
|
|
66
89
|
:root {
|
|
90
|
+
--atbl-control-text-size: var(--atbl-type-body-size);
|
|
67
91
|
--atbl-background: var(--atbl-palette-background);
|
|
68
92
|
--atbl-surface: var(--atbl-palette-surface);
|
|
69
93
|
--atbl-subtle: var(--atbl-palette-subtle);
|
|
@@ -812,14 +836,10 @@ var connectionView = defineDataView({
|
|
|
812
836
|
isEmpty: () => false,
|
|
813
837
|
emptyFallback: { title: "No connection" }
|
|
814
838
|
});
|
|
815
|
-
function GettingStarted({
|
|
816
|
-
config,
|
|
817
|
-
dataContext
|
|
818
|
-
}) {
|
|
839
|
+
function GettingStarted({ dataContext }) {
|
|
819
840
|
const connection = useDeclaredResult(connectionView);
|
|
820
841
|
const state = connection.view.kind === "error" || connection.view.kind === "stale-error" ? "error" : connection.refreshing || !connection.snapshot ? "checking" : "connected";
|
|
821
842
|
return /* @__PURE__ */ jsxs(DataApp, {
|
|
822
|
-
config,
|
|
823
843
|
dataContext,
|
|
824
844
|
description: "Check your lakehouse connection, then build a view around a real question.",
|
|
825
845
|
queries: connection.queries,
|
|
@@ -1064,7 +1084,376 @@ function GlossaryDefinition({
|
|
|
1064
1084
|
});
|
|
1065
1085
|
}
|
|
1066
1086
|
// src/react/ui/TrendChart.tsx
|
|
1067
|
-
import { useLayoutEffect, useRef } from "react";
|
|
1087
|
+
import { useLayoutEffect, useRef, useState as useState2 } from "react";
|
|
1088
|
+
import {
|
|
1089
|
+
AreaChart as RechartsAreaChart,
|
|
1090
|
+
LineChart as RechartsLineChart,
|
|
1091
|
+
ReferenceLine
|
|
1092
|
+
} from "recharts";
|
|
1093
|
+
|
|
1094
|
+
// src/react/ui/chart-primitives.tsx
|
|
1095
|
+
import {
|
|
1096
|
+
Area,
|
|
1097
|
+
Dot,
|
|
1098
|
+
Bar,
|
|
1099
|
+
Line,
|
|
1100
|
+
Scatter,
|
|
1101
|
+
XAxis,
|
|
1102
|
+
YAxis,
|
|
1103
|
+
CartesianGrid,
|
|
1104
|
+
Tooltip,
|
|
1105
|
+
DefaultTooltipContent
|
|
1106
|
+
} from "recharts";
|
|
1107
|
+
|
|
1108
|
+
// src/react/ui/ChartLegend.tsx
|
|
1109
|
+
import {
|
|
1110
|
+
Children,
|
|
1111
|
+
useId,
|
|
1112
|
+
useState
|
|
1113
|
+
} from "react";
|
|
1114
|
+
import { jsx as jsx6, jsxs as jsxs4, Fragment } from "react/jsx-runtime";
|
|
1115
|
+
function ChartLegendRoot({
|
|
1116
|
+
children,
|
|
1117
|
+
layout = "horizontal",
|
|
1118
|
+
align = "start",
|
|
1119
|
+
maxVisibleItems = 6,
|
|
1120
|
+
className,
|
|
1121
|
+
"aria-label": ariaLabel = "Chart legend",
|
|
1122
|
+
...props
|
|
1123
|
+
}) {
|
|
1124
|
+
const [expanded, setExpanded] = useState(false);
|
|
1125
|
+
const listId = useId();
|
|
1126
|
+
const items = Children.toArray(children);
|
|
1127
|
+
const limit = Number.isFinite(maxVisibleItems) ? Math.max(1, Math.floor(maxVisibleItems)) : 6;
|
|
1128
|
+
const visibleCount = items.length > limit ? limit - 1 : limit;
|
|
1129
|
+
const hiddenCount = Math.max(0, items.length - visibleCount);
|
|
1130
|
+
return /* @__PURE__ */ jsx6("div", {
|
|
1131
|
+
className: "altertable-chart-legend-container",
|
|
1132
|
+
children: /* @__PURE__ */ jsxs4("ul", {
|
|
1133
|
+
id: listId,
|
|
1134
|
+
...props,
|
|
1135
|
+
"aria-label": ariaLabel,
|
|
1136
|
+
"data-layout": layout,
|
|
1137
|
+
"data-align": align,
|
|
1138
|
+
className: classNames("altertable-chart-legend", className),
|
|
1139
|
+
children: [
|
|
1140
|
+
expanded ? items : items.slice(0, visibleCount),
|
|
1141
|
+
hiddenCount > 0 && /* @__PURE__ */ jsx6("li", {
|
|
1142
|
+
className: "altertable-chart-legend-overflow",
|
|
1143
|
+
children: /* @__PURE__ */ jsx6("button", {
|
|
1144
|
+
type: "button",
|
|
1145
|
+
"data-atbl-control": "action",
|
|
1146
|
+
"data-atbl-focus": "ring",
|
|
1147
|
+
className: "altertable-chart-legend-action",
|
|
1148
|
+
"aria-expanded": expanded,
|
|
1149
|
+
"aria-controls": props.id ?? listId,
|
|
1150
|
+
onClick: () => setExpanded((value) => !value),
|
|
1151
|
+
children: expanded ? "Show fewer" : `+${hiddenCount} more`
|
|
1152
|
+
})
|
|
1153
|
+
})
|
|
1154
|
+
]
|
|
1155
|
+
})
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
function ChartLegendItem({
|
|
1159
|
+
inactive,
|
|
1160
|
+
className,
|
|
1161
|
+
...props
|
|
1162
|
+
}) {
|
|
1163
|
+
return /* @__PURE__ */ jsx6("li", {
|
|
1164
|
+
...props,
|
|
1165
|
+
"data-inactive": inactive || undefined,
|
|
1166
|
+
className: classNames("altertable-chart-legend-item", className)
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
function ChartLegendMarker({
|
|
1170
|
+
kind,
|
|
1171
|
+
color = kind === "bar" ? chartBarFill : "var(--atbl-accent)",
|
|
1172
|
+
className,
|
|
1173
|
+
style,
|
|
1174
|
+
...props
|
|
1175
|
+
}) {
|
|
1176
|
+
return /* @__PURE__ */ jsxs4("svg", {
|
|
1177
|
+
...props,
|
|
1178
|
+
"aria-hidden": "true",
|
|
1179
|
+
focusable: "false",
|
|
1180
|
+
viewBox: kind === "square" ? "0 0 8 8" : "0 0 20 12",
|
|
1181
|
+
"data-kind": kind,
|
|
1182
|
+
className: classNames("altertable-chart-legend-marker", className),
|
|
1183
|
+
style: { color, ...style },
|
|
1184
|
+
children: [
|
|
1185
|
+
kind === "square" && /* @__PURE__ */ jsx6("rect", {
|
|
1186
|
+
width: "8",
|
|
1187
|
+
height: "8",
|
|
1188
|
+
fill: "currentColor"
|
|
1189
|
+
}),
|
|
1190
|
+
kind === "bar" && /* @__PURE__ */ jsx6("path", {
|
|
1191
|
+
d: "M6 11V3Q6 1 8 1H12Q14 1 14 3V11Z",
|
|
1192
|
+
fill: "currentColor"
|
|
1193
|
+
}),
|
|
1194
|
+
kind === "slice" && /* @__PURE__ */ jsx6("rect", {
|
|
1195
|
+
x: "5",
|
|
1196
|
+
y: "1",
|
|
1197
|
+
width: "10",
|
|
1198
|
+
height: "10",
|
|
1199
|
+
rx: "2",
|
|
1200
|
+
fill: "currentColor"
|
|
1201
|
+
}),
|
|
1202
|
+
kind === "point" && /* @__PURE__ */ jsx6("circle", {
|
|
1203
|
+
cx: "10",
|
|
1204
|
+
cy: "6",
|
|
1205
|
+
r: "3",
|
|
1206
|
+
fill: "currentColor"
|
|
1207
|
+
}),
|
|
1208
|
+
kind === "line" && /* @__PURE__ */ jsxs4(Fragment, {
|
|
1209
|
+
children: [
|
|
1210
|
+
/* @__PURE__ */ jsx6("path", {
|
|
1211
|
+
d: "M1 6H19",
|
|
1212
|
+
fill: "none",
|
|
1213
|
+
stroke: "currentColor",
|
|
1214
|
+
strokeWidth: "2",
|
|
1215
|
+
strokeLinecap: "round"
|
|
1216
|
+
}),
|
|
1217
|
+
/* @__PURE__ */ jsx6("circle", {
|
|
1218
|
+
cx: "10",
|
|
1219
|
+
cy: "6",
|
|
1220
|
+
r: "2.5",
|
|
1221
|
+
fill: "var(--atbl-surface)",
|
|
1222
|
+
stroke: "currentColor",
|
|
1223
|
+
strokeWidth: "2"
|
|
1224
|
+
})
|
|
1225
|
+
]
|
|
1226
|
+
}),
|
|
1227
|
+
kind === "area" && /* @__PURE__ */ jsxs4(Fragment, {
|
|
1228
|
+
children: [
|
|
1229
|
+
/* @__PURE__ */ jsx6("path", {
|
|
1230
|
+
d: "M1 10L7 5L13 7L19 2V11H1Z",
|
|
1231
|
+
fill: "currentColor",
|
|
1232
|
+
fillOpacity: "0.14"
|
|
1233
|
+
}),
|
|
1234
|
+
/* @__PURE__ */ jsx6("path", {
|
|
1235
|
+
d: "M1 10L7 5L13 7L19 2",
|
|
1236
|
+
fill: "none",
|
|
1237
|
+
stroke: "currentColor",
|
|
1238
|
+
strokeWidth: "2",
|
|
1239
|
+
strokeLinejoin: "round",
|
|
1240
|
+
strokeLinecap: "round"
|
|
1241
|
+
})
|
|
1242
|
+
]
|
|
1243
|
+
})
|
|
1244
|
+
]
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
function ChartLegendLabel({
|
|
1248
|
+
className,
|
|
1249
|
+
children,
|
|
1250
|
+
title,
|
|
1251
|
+
...props
|
|
1252
|
+
}) {
|
|
1253
|
+
return /* @__PURE__ */ jsx6("span", {
|
|
1254
|
+
...props,
|
|
1255
|
+
title: title ?? (typeof children === "string" ? children : undefined),
|
|
1256
|
+
className: classNames("altertable-chart-legend-label", className),
|
|
1257
|
+
children
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
var ChartLegend = Object.assign(ChartLegendRoot, {
|
|
1261
|
+
Item: ChartLegendItem,
|
|
1262
|
+
Marker: ChartLegendMarker,
|
|
1263
|
+
Label: ChartLegendLabel
|
|
1264
|
+
});
|
|
1265
|
+
|
|
1266
|
+
// src/react/ui/chart-primitives.tsx
|
|
1267
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1268
|
+
var accent = "var(--atbl-accent)";
|
|
1269
|
+
function ChartDot({
|
|
1270
|
+
active = false,
|
|
1271
|
+
stroke = accent,
|
|
1272
|
+
fill = active ? stroke : "var(--atbl-surface)",
|
|
1273
|
+
r = active ? 4.5 : 3,
|
|
1274
|
+
...props
|
|
1275
|
+
}) {
|
|
1276
|
+
return /* @__PURE__ */ jsx7(Dot, {
|
|
1277
|
+
stroke,
|
|
1278
|
+
fill,
|
|
1279
|
+
r,
|
|
1280
|
+
strokeWidth: 2,
|
|
1281
|
+
...props
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
function ChartBar(props) {
|
|
1285
|
+
return /* @__PURE__ */ jsx7(Bar, {
|
|
1286
|
+
fill: chartBarFill,
|
|
1287
|
+
radius: [5, 5, 0, 0],
|
|
1288
|
+
maxBarSize: 30,
|
|
1289
|
+
activeBar: { fill: accent },
|
|
1290
|
+
isAnimationActive: false,
|
|
1291
|
+
...props
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
function ChartLine({
|
|
1295
|
+
stroke = accent,
|
|
1296
|
+
dot,
|
|
1297
|
+
activeDot,
|
|
1298
|
+
...props
|
|
1299
|
+
}) {
|
|
1300
|
+
return /* @__PURE__ */ jsx7(Line, {
|
|
1301
|
+
type: "linear",
|
|
1302
|
+
stroke,
|
|
1303
|
+
strokeWidth: 2,
|
|
1304
|
+
strokeLinecap: "round",
|
|
1305
|
+
strokeLinejoin: "round",
|
|
1306
|
+
dot: dot === undefined || dot === true ? ({ cx, cy }) => /* @__PURE__ */ jsx7(ChartDot, {
|
|
1307
|
+
cx,
|
|
1308
|
+
cy,
|
|
1309
|
+
stroke
|
|
1310
|
+
}) : dot,
|
|
1311
|
+
activeDot: activeDot === undefined || activeDot === true ? ({ cx, cy }) => /* @__PURE__ */ jsx7(ChartDot, {
|
|
1312
|
+
cx,
|
|
1313
|
+
cy,
|
|
1314
|
+
stroke,
|
|
1315
|
+
active: true
|
|
1316
|
+
}) : activeDot,
|
|
1317
|
+
isAnimationActive: false,
|
|
1318
|
+
...props
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
function ChartArea({
|
|
1322
|
+
stroke = accent,
|
|
1323
|
+
dot,
|
|
1324
|
+
activeDot,
|
|
1325
|
+
...props
|
|
1326
|
+
}) {
|
|
1327
|
+
return /* @__PURE__ */ jsx7(Area, {
|
|
1328
|
+
legendType: "square",
|
|
1329
|
+
type: "linear",
|
|
1330
|
+
stroke,
|
|
1331
|
+
strokeWidth: 2,
|
|
1332
|
+
strokeLinecap: "round",
|
|
1333
|
+
strokeLinejoin: "round",
|
|
1334
|
+
fill: stroke,
|
|
1335
|
+
fillOpacity: 0.14,
|
|
1336
|
+
dot: dot === undefined || dot === true ? ({ cx, cy }) => /* @__PURE__ */ jsx7(ChartDot, {
|
|
1337
|
+
cx,
|
|
1338
|
+
cy,
|
|
1339
|
+
stroke
|
|
1340
|
+
}) : dot,
|
|
1341
|
+
activeDot: activeDot === undefined || activeDot === true ? ({ cx, cy }) => /* @__PURE__ */ jsx7(ChartDot, {
|
|
1342
|
+
cx,
|
|
1343
|
+
cy,
|
|
1344
|
+
stroke,
|
|
1345
|
+
active: true
|
|
1346
|
+
}) : activeDot,
|
|
1347
|
+
isAnimationActive: false,
|
|
1348
|
+
...props
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
function ChartScatter(props) {
|
|
1352
|
+
return /* @__PURE__ */ jsx7(Scatter, {
|
|
1353
|
+
fill: accent,
|
|
1354
|
+
isAnimationActive: false,
|
|
1355
|
+
...props
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
function ChartXAxis(props) {
|
|
1359
|
+
return /* @__PURE__ */ jsx7(XAxis, {
|
|
1360
|
+
stroke: "var(--atbl-muted)",
|
|
1361
|
+
tickLine: false,
|
|
1362
|
+
axisLine: { stroke: "var(--atbl-border)" },
|
|
1363
|
+
tick: { fontSize: "var(--atbl-type-meta-size)" },
|
|
1364
|
+
...props
|
|
1365
|
+
});
|
|
1366
|
+
}
|
|
1367
|
+
function ChartYAxis(props) {
|
|
1368
|
+
return /* @__PURE__ */ jsx7(YAxis, {
|
|
1369
|
+
stroke: "var(--atbl-muted)",
|
|
1370
|
+
tickLine: false,
|
|
1371
|
+
axisLine: false,
|
|
1372
|
+
tick: { fontSize: "var(--atbl-type-meta-size)" },
|
|
1373
|
+
...props
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
function ChartGrid(props) {
|
|
1377
|
+
return /* @__PURE__ */ jsx7(CartesianGrid, {
|
|
1378
|
+
stroke: "var(--atbl-border)",
|
|
1379
|
+
vertical: false,
|
|
1380
|
+
...props
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
function ChartTooltipContent(props) {
|
|
1384
|
+
if (!props.active)
|
|
1385
|
+
return null;
|
|
1386
|
+
return /* @__PURE__ */ jsx7(TooltipSurface, {
|
|
1387
|
+
"data-variant": "chart",
|
|
1388
|
+
role: "tooltip",
|
|
1389
|
+
children: /* @__PURE__ */ jsx7(DefaultTooltipContent, {
|
|
1390
|
+
...props,
|
|
1391
|
+
separator: "",
|
|
1392
|
+
payload: props.payload?.map((entry) => ({
|
|
1393
|
+
...entry,
|
|
1394
|
+
unit: undefined,
|
|
1395
|
+
formatter: (value, name, _item, index, payload) => {
|
|
1396
|
+
const formatter = entry.formatter ?? props.formatter;
|
|
1397
|
+
const formatted = formatter ? formatter(value, name, entry, index, props.payload ?? payload) : Array.isArray(value) ? value.join(" ~ ") : value;
|
|
1398
|
+
if (formatted == null)
|
|
1399
|
+
return null;
|
|
1400
|
+
const [displayValue, displayName] = Array.isArray(formatted) ? formatted : [formatted, name];
|
|
1401
|
+
return [
|
|
1402
|
+
/* @__PURE__ */ jsxs5("span", {
|
|
1403
|
+
className: "altertable-chart-tooltip-row",
|
|
1404
|
+
children: [
|
|
1405
|
+
/* @__PURE__ */ jsx7(ChartLegend.Marker, {
|
|
1406
|
+
kind: "square",
|
|
1407
|
+
className: "altertable-chart-tooltip-marker",
|
|
1408
|
+
color: entry.color ?? entry.stroke ?? entry.fill ?? "var(--atbl-accent)"
|
|
1409
|
+
}),
|
|
1410
|
+
/* @__PURE__ */ jsxs5("span", {
|
|
1411
|
+
className: "altertable-chart-tooltip-reading",
|
|
1412
|
+
children: [
|
|
1413
|
+
displayName,
|
|
1414
|
+
displayName !== undefined && displayName !== "" ? props.separator ?? " : " : "",
|
|
1415
|
+
displayValue,
|
|
1416
|
+
entry.unit
|
|
1417
|
+
]
|
|
1418
|
+
})
|
|
1419
|
+
]
|
|
1420
|
+
}, entry.graphicalItemId),
|
|
1421
|
+
""
|
|
1422
|
+
];
|
|
1423
|
+
}
|
|
1424
|
+
})),
|
|
1425
|
+
contentStyle: {
|
|
1426
|
+
margin: 0,
|
|
1427
|
+
padding: 0,
|
|
1428
|
+
background: "transparent",
|
|
1429
|
+
border: 0,
|
|
1430
|
+
whiteSpace: "normal",
|
|
1431
|
+
...props.contentStyle
|
|
1432
|
+
},
|
|
1433
|
+
labelStyle: {
|
|
1434
|
+
color: "var(--atbl-text)",
|
|
1435
|
+
fontSize: "var(--atbl-type-body-size)",
|
|
1436
|
+
fontWeight: 650,
|
|
1437
|
+
lineHeight: 1.2,
|
|
1438
|
+
...props.labelStyle
|
|
1439
|
+
},
|
|
1440
|
+
itemStyle: {
|
|
1441
|
+
color: "var(--atbl-text)",
|
|
1442
|
+
paddingTop: 2,
|
|
1443
|
+
paddingBottom: 0,
|
|
1444
|
+
...props.itemStyle
|
|
1445
|
+
}
|
|
1446
|
+
})
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
function ChartTooltip(props) {
|
|
1450
|
+
return /* @__PURE__ */ jsx7(Tooltip, {
|
|
1451
|
+
content: /* @__PURE__ */ jsx7(ChartTooltipContent, {}),
|
|
1452
|
+
cursor: false,
|
|
1453
|
+
isAnimationActive: false,
|
|
1454
|
+
...props
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1068
1457
|
|
|
1069
1458
|
// src/react/ui/chart-data.ts
|
|
1070
1459
|
function validateChartItems(kind, items) {
|
|
@@ -1085,7 +1474,7 @@ function validateChartItems(kind, items) {
|
|
|
1085
1474
|
}
|
|
1086
1475
|
|
|
1087
1476
|
// src/react/ui/TrendChart.tsx
|
|
1088
|
-
import { jsx as
|
|
1477
|
+
import { jsx as jsx8, jsxs as jsxs6, Fragment as Fragment2 } from "react/jsx-runtime";
|
|
1089
1478
|
function TrendChart({
|
|
1090
1479
|
items,
|
|
1091
1480
|
unit,
|
|
@@ -1094,6 +1483,8 @@ function TrendChart({
|
|
|
1094
1483
|
area = false
|
|
1095
1484
|
}) {
|
|
1096
1485
|
validateChartItems(area ? "area" : "line", items);
|
|
1486
|
+
const Plot = area ? RechartsAreaChart : RechartsLineChart;
|
|
1487
|
+
const [activeIndex, setActiveIndex] = useState2();
|
|
1097
1488
|
const scrollport = useRef(null);
|
|
1098
1489
|
useLayoutEffect(() => {
|
|
1099
1490
|
const element = scrollport.current;
|
|
@@ -1103,67 +1494,80 @@ function TrendChart({
|
|
|
1103
1494
|
const magnitude = items.reduce((max, item) => Math.max(max, Math.abs(item.value)), 1);
|
|
1104
1495
|
const low = items.reduce((min, item) => Math.min(min, item.value / magnitude), 0);
|
|
1105
1496
|
const high = items.reduce((max, item) => Math.max(max, item.value / magnitude), 0);
|
|
1106
|
-
|
|
1107
|
-
function y(value) {
|
|
1108
|
-
return 208 - (value / magnitude - low) / span * 196;
|
|
1109
|
-
}
|
|
1110
|
-
const points = items.map((item, index) => ({
|
|
1111
|
-
x: (index + 0.5) / items.length * 1000,
|
|
1112
|
-
y: y(item.value)
|
|
1113
|
-
}));
|
|
1114
|
-
const path = points.map((point, index) => `${index ? "L" : "M"}${point.x},${point.y}`).join(" ");
|
|
1115
|
-
const first = points[0];
|
|
1116
|
-
const last = points.at(-1);
|
|
1117
|
-
return /* @__PURE__ */ jsxs4("section", {
|
|
1497
|
+
return /* @__PURE__ */ jsxs6("section", {
|
|
1118
1498
|
className: "altertable-trend-chart",
|
|
1119
1499
|
"aria-label": ariaLabel,
|
|
1120
1500
|
children: [
|
|
1121
|
-
items.length === 0 && /* @__PURE__ */
|
|
1501
|
+
items.length === 0 && /* @__PURE__ */ jsx8("span", {
|
|
1122
1502
|
children: "No data"
|
|
1123
1503
|
}),
|
|
1124
|
-
items.length > 0 && /* @__PURE__ */
|
|
1125
|
-
children: /* @__PURE__ */
|
|
1504
|
+
items.length > 0 && /* @__PURE__ */ jsx8(Fragment2, {
|
|
1505
|
+
children: /* @__PURE__ */ jsx8("div", {
|
|
1126
1506
|
className: "altertable-trend-scroll",
|
|
1127
1507
|
ref: scrollport,
|
|
1128
|
-
children: /* @__PURE__ */
|
|
1508
|
+
children: /* @__PURE__ */ jsxs6("div", {
|
|
1129
1509
|
className: "altertable-trend-plot",
|
|
1130
1510
|
style: { minWidth: items.length * 40 },
|
|
1131
1511
|
children: [
|
|
1132
|
-
/* @__PURE__ */
|
|
1133
|
-
|
|
1134
|
-
preserveAspectRatio: "none",
|
|
1512
|
+
/* @__PURE__ */ jsx8("div", {
|
|
1513
|
+
className: "altertable-chart-canvas",
|
|
1135
1514
|
"aria-hidden": "true",
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1515
|
+
children: /* @__PURE__ */ jsxs6(Plot, {
|
|
1516
|
+
data: items.map((item) => ({
|
|
1517
|
+
...item,
|
|
1518
|
+
plotValue: item.value / magnitude
|
|
1519
|
+
})),
|
|
1520
|
+
title: ariaLabel,
|
|
1521
|
+
responsive: true,
|
|
1522
|
+
width: "100%",
|
|
1523
|
+
height: 220,
|
|
1524
|
+
margin: { top: 12, right: 0, bottom: 12, left: 0 },
|
|
1525
|
+
accessibilityLayer: false,
|
|
1526
|
+
tabIndex: -1,
|
|
1527
|
+
children: [
|
|
1528
|
+
/* @__PURE__ */ jsx8(ChartXAxis, {
|
|
1529
|
+
hide: true,
|
|
1530
|
+
dataKey: "id",
|
|
1531
|
+
scale: "band"
|
|
1532
|
+
}),
|
|
1533
|
+
/* @__PURE__ */ jsx8(ChartYAxis, {
|
|
1534
|
+
hide: true,
|
|
1535
|
+
domain: [low, high === low ? low + 1 : high]
|
|
1536
|
+
}),
|
|
1537
|
+
/* @__PURE__ */ jsx8(ReferenceLine, {
|
|
1538
|
+
y: 0,
|
|
1539
|
+
stroke: "var(--atbl-border)"
|
|
1540
|
+
}),
|
|
1541
|
+
area && /* @__PURE__ */ jsx8(ChartArea, {
|
|
1542
|
+
dataKey: "plotValue",
|
|
1543
|
+
stroke: "none",
|
|
1544
|
+
fill: "var(--atbl-accent)",
|
|
1545
|
+
dot: false,
|
|
1546
|
+
activeDot: false
|
|
1547
|
+
}),
|
|
1548
|
+
/* @__PURE__ */ jsx8(ChartLine, {
|
|
1549
|
+
dataKey: "plotValue",
|
|
1550
|
+
dot: ({ cx, cy, index }) => /* @__PURE__ */ jsx8(ChartDot, {
|
|
1551
|
+
cx,
|
|
1552
|
+
cy,
|
|
1553
|
+
active: index === activeIndex
|
|
1554
|
+
}),
|
|
1555
|
+
activeDot: false
|
|
1556
|
+
})
|
|
1557
|
+
]
|
|
1558
|
+
})
|
|
1156
1559
|
}),
|
|
1157
|
-
/* @__PURE__ */
|
|
1560
|
+
/* @__PURE__ */ jsx8("div", {
|
|
1158
1561
|
className: "altertable-trend-points",
|
|
1159
|
-
children: items.map((item, index) => /* @__PURE__ */
|
|
1562
|
+
children: items.map((item, index) => /* @__PURE__ */ jsx8(Tooltip2, {
|
|
1160
1563
|
variant: "chart",
|
|
1161
|
-
|
|
1564
|
+
onOpenChange: (open) => setActiveIndex((current) => open ? index : current === index ? undefined : current),
|
|
1565
|
+
content: /* @__PURE__ */ jsxs6(Fragment2, {
|
|
1162
1566
|
children: [
|
|
1163
|
-
/* @__PURE__ */
|
|
1567
|
+
/* @__PURE__ */ jsx8("strong", {
|
|
1164
1568
|
children: item.label
|
|
1165
1569
|
}),
|
|
1166
|
-
/* @__PURE__ */
|
|
1570
|
+
/* @__PURE__ */ jsxs6("span", {
|
|
1167
1571
|
children: [
|
|
1168
1572
|
formatValue(item.value),
|
|
1169
1573
|
" ",
|
|
@@ -1173,22 +1577,18 @@ function TrendChart({
|
|
|
1173
1577
|
]
|
|
1174
1578
|
}),
|
|
1175
1579
|
className: "altertable-chart-column",
|
|
1176
|
-
children: /* @__PURE__ */
|
|
1580
|
+
children: /* @__PURE__ */ jsxs6("button", {
|
|
1177
1581
|
"data-atbl-focus": "ring",
|
|
1178
|
-
"data-atbl-control": "
|
|
1582
|
+
"data-atbl-control": "default",
|
|
1179
1583
|
type: "button",
|
|
1180
1584
|
tabIndex: -1,
|
|
1181
1585
|
className: "altertable-trend-point",
|
|
1182
1586
|
"aria-label": `${item.label}: ${formatValue(item.value)} ${unit}`,
|
|
1183
1587
|
children: [
|
|
1184
|
-
/* @__PURE__ */
|
|
1185
|
-
className: "altertable-trend-hit"
|
|
1186
|
-
children: /* @__PURE__ */ jsx6("span", {
|
|
1187
|
-
className: "altertable-trend-dot",
|
|
1188
|
-
style: { top: `${points[index].y / 220 * 100}%` }
|
|
1189
|
-
})
|
|
1588
|
+
/* @__PURE__ */ jsx8("span", {
|
|
1589
|
+
className: "altertable-trend-hit"
|
|
1190
1590
|
}),
|
|
1191
|
-
/* @__PURE__ */
|
|
1591
|
+
/* @__PURE__ */ jsx8("span", {
|
|
1192
1592
|
className: "altertable-trend-label",
|
|
1193
1593
|
children: item.label
|
|
1194
1594
|
})
|
|
@@ -1205,33 +1605,34 @@ function TrendChart({
|
|
|
1205
1605
|
}
|
|
1206
1606
|
|
|
1207
1607
|
// src/react/ui/LineChart.tsx
|
|
1208
|
-
import { jsx as
|
|
1608
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1209
1609
|
function LineChart(props) {
|
|
1210
|
-
return /* @__PURE__ */
|
|
1610
|
+
return /* @__PURE__ */ jsx9(TrendChart, {
|
|
1211
1611
|
...props
|
|
1212
1612
|
});
|
|
1213
1613
|
}
|
|
1214
1614
|
// src/react/ui/AreaChart.tsx
|
|
1215
|
-
import { jsx as
|
|
1615
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1216
1616
|
function AreaChart(props) {
|
|
1217
|
-
return /* @__PURE__ */
|
|
1617
|
+
return /* @__PURE__ */ jsx10(TrendChart, {
|
|
1218
1618
|
...props,
|
|
1219
1619
|
area: true
|
|
1220
1620
|
});
|
|
1221
1621
|
}
|
|
1222
1622
|
// src/react/ui/PieChart.tsx
|
|
1223
|
-
import { useState } from "react";
|
|
1224
|
-
import { jsx as
|
|
1623
|
+
import { useState as useState3 } from "react";
|
|
1624
|
+
import { jsx as jsx11, jsxs as jsxs7, Fragment as Fragment3 } from "react/jsx-runtime";
|
|
1225
1625
|
function PieChart({
|
|
1226
1626
|
items,
|
|
1227
1627
|
unit,
|
|
1228
1628
|
ariaLabel,
|
|
1629
|
+
showLegend = true,
|
|
1229
1630
|
formatValue = (value) => new Intl.NumberFormat().format(value)
|
|
1230
1631
|
}) {
|
|
1231
1632
|
validateChartItems("pie", items);
|
|
1232
|
-
const [previewId, setPreviewId] =
|
|
1233
|
-
const [plotOpen, setPlotOpen] =
|
|
1234
|
-
const [legendId, setLegendId] =
|
|
1633
|
+
const [previewId, setPreviewId] = useState3(null);
|
|
1634
|
+
const [plotOpen, setPlotOpen] = useState3(false);
|
|
1635
|
+
const [legendId, setLegendId] = useState3(null);
|
|
1235
1636
|
const maximum = items.reduce((max, item) => Math.max(max, item.value), 0);
|
|
1236
1637
|
const total = maximum ? items.reduce((sum, item) => sum + item.value / maximum, 0) : 0;
|
|
1237
1638
|
let cursor = -Math.PI / 2;
|
|
@@ -1258,12 +1659,12 @@ function PieChart({
|
|
|
1258
1659
|
}).format(share);
|
|
1259
1660
|
}
|
|
1260
1661
|
function detail(item) {
|
|
1261
|
-
return /* @__PURE__ */
|
|
1662
|
+
return /* @__PURE__ */ jsxs7(Fragment3, {
|
|
1262
1663
|
children: [
|
|
1263
|
-
/* @__PURE__ */
|
|
1664
|
+
/* @__PURE__ */ jsx11("strong", {
|
|
1264
1665
|
children: item.label
|
|
1265
1666
|
}),
|
|
1266
|
-
/* @__PURE__ */
|
|
1667
|
+
/* @__PURE__ */ jsxs7("span", {
|
|
1267
1668
|
children: [
|
|
1268
1669
|
formatValue(item.value),
|
|
1269
1670
|
" ",
|
|
@@ -1276,37 +1677,37 @@ function PieChart({
|
|
|
1276
1677
|
});
|
|
1277
1678
|
}
|
|
1278
1679
|
const preview = slices.find((item) => item.id === previewId);
|
|
1279
|
-
return /* @__PURE__ */
|
|
1680
|
+
return /* @__PURE__ */ jsxs7("section", {
|
|
1280
1681
|
className: "altertable-pie-chart",
|
|
1281
1682
|
"aria-label": ariaLabel,
|
|
1282
1683
|
children: [
|
|
1283
|
-
items.length === 0 && /* @__PURE__ */
|
|
1684
|
+
items.length === 0 && /* @__PURE__ */ jsx11("span", {
|
|
1284
1685
|
children: "No data"
|
|
1285
1686
|
}),
|
|
1286
|
-
items.length > 0 && /* @__PURE__ */
|
|
1687
|
+
items.length > 0 && /* @__PURE__ */ jsxs7(Fragment3, {
|
|
1287
1688
|
children: [
|
|
1288
|
-
/* @__PURE__ */
|
|
1689
|
+
/* @__PURE__ */ jsxs7("div", {
|
|
1289
1690
|
className: "altertable-pie-plot",
|
|
1290
1691
|
children: [
|
|
1291
|
-
/* @__PURE__ */
|
|
1692
|
+
/* @__PURE__ */ jsx11(Tooltip2, {
|
|
1292
1693
|
variant: "chart",
|
|
1293
1694
|
className: "altertable-pie-tooltip",
|
|
1294
1695
|
onOpenChange: setPlotOpen,
|
|
1295
1696
|
content: preview ? detail(preview) : null,
|
|
1296
|
-
children: /* @__PURE__ */
|
|
1697
|
+
children: /* @__PURE__ */ jsx11("svg", {
|
|
1297
1698
|
viewBox: "0 0 220 220",
|
|
1298
1699
|
"aria-hidden": "true",
|
|
1299
1700
|
focusable: "false",
|
|
1300
|
-
children: total === 0 ? /* @__PURE__ */
|
|
1701
|
+
children: total === 0 ? /* @__PURE__ */ jsx11("circle", {
|
|
1301
1702
|
cx: "110",
|
|
1302
1703
|
cy: "110",
|
|
1303
1704
|
r: "100",
|
|
1304
1705
|
className: "altertable-pie-empty"
|
|
1305
|
-
}) : slices.filter((item) => item.share > 0).map((item) => /* @__PURE__ */
|
|
1706
|
+
}) : slices.filter((item) => item.share > 0).map((item) => /* @__PURE__ */ jsx11("path", {
|
|
1306
1707
|
d: item.path,
|
|
1307
1708
|
fill: item.color,
|
|
1308
1709
|
className: "altertable-pie-slice",
|
|
1309
|
-
"data-active": item.id === (plotOpen && previewId ? previewId : legendId) || undefined,
|
|
1710
|
+
"data-active": item.id === (plotOpen && previewId ? previewId : showLegend ? legendId : null) || undefined,
|
|
1310
1711
|
onPointerDown: (event) => {
|
|
1311
1712
|
if (plotOpen && previewId !== item.id)
|
|
1312
1713
|
event.preventDefault();
|
|
@@ -1323,40 +1724,44 @@ function PieChart({
|
|
|
1323
1724
|
}, item.id))
|
|
1324
1725
|
})
|
|
1325
1726
|
}),
|
|
1326
|
-
total === 0 && /* @__PURE__ */
|
|
1727
|
+
total === 0 && /* @__PURE__ */ jsx11("span", {
|
|
1327
1728
|
className: "altertable-pie-empty-label",
|
|
1328
1729
|
children: "No nonzero values"
|
|
1329
1730
|
})
|
|
1330
1731
|
]
|
|
1331
1732
|
}),
|
|
1332
|
-
/* @__PURE__ */
|
|
1733
|
+
showLegend && /* @__PURE__ */ jsx11(ChartLegend, {
|
|
1734
|
+
layout: "vertical",
|
|
1735
|
+
"aria-label": `${ariaLabel} legend`,
|
|
1333
1736
|
className: "altertable-pie-legend",
|
|
1334
|
-
children: slices.map((item) => /* @__PURE__ */
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1737
|
+
children: slices.map((item) => /* @__PURE__ */ jsx11(ChartLegend.Item, {
|
|
1738
|
+
children: /* @__PURE__ */ jsx11(Tooltip2, {
|
|
1739
|
+
variant: "chart",
|
|
1740
|
+
content: detail(item),
|
|
1741
|
+
onOpenChange: (open) => setLegendId((current) => open ? item.id : current === item.id ? null : current),
|
|
1742
|
+
children: /* @__PURE__ */ jsxs7("button", {
|
|
1743
|
+
"data-atbl-focus": "ring",
|
|
1744
|
+
"data-atbl-control": "default",
|
|
1745
|
+
type: "button",
|
|
1746
|
+
tabIndex: -1,
|
|
1747
|
+
"aria-label": `${item.label}: ${formatValue(item.value)} ${unit}, ${percent(item.share)}`,
|
|
1748
|
+
children: [
|
|
1749
|
+
/* @__PURE__ */ jsx11(ChartLegend.Marker, {
|
|
1750
|
+
kind: "square",
|
|
1751
|
+
color: item.color
|
|
1752
|
+
}),
|
|
1753
|
+
/* @__PURE__ */ jsx11(ChartLegend.Label, {
|
|
1754
|
+
className: "altertable-pie-label",
|
|
1755
|
+
children: item.label
|
|
1756
|
+
}),
|
|
1757
|
+
/* @__PURE__ */ jsx11("strong", {
|
|
1758
|
+
children: formatValue(item.value)
|
|
1759
|
+
}),
|
|
1760
|
+
/* @__PURE__ */ jsx11("span", {
|
|
1761
|
+
children: percent(item.share)
|
|
1762
|
+
})
|
|
1763
|
+
]
|
|
1764
|
+
})
|
|
1360
1765
|
})
|
|
1361
1766
|
}, item.id))
|
|
1362
1767
|
})
|
|
@@ -1366,7 +1771,7 @@ function PieChart({
|
|
|
1366
1771
|
});
|
|
1367
1772
|
}
|
|
1368
1773
|
// src/react/ui/ScatterChart.tsx
|
|
1369
|
-
import { jsx as
|
|
1774
|
+
import { jsx as jsx12, jsxs as jsxs8, Fragment as Fragment4 } from "react/jsx-runtime";
|
|
1370
1775
|
function formatNumber(value) {
|
|
1371
1776
|
return new Intl.NumberFormat(undefined, { maximumFractionDigits: 1 }).format(value);
|
|
1372
1777
|
}
|
|
@@ -1395,37 +1800,37 @@ function ScatterChart({
|
|
|
1395
1800
|
validateChartItems("scatter", items);
|
|
1396
1801
|
const xScale = scale(items.map((item) => item.x));
|
|
1397
1802
|
const yScale = scale(items.map((item) => item.y));
|
|
1398
|
-
return /* @__PURE__ */
|
|
1803
|
+
return /* @__PURE__ */ jsxs8("section", {
|
|
1399
1804
|
className: "altertable-scatter-chart",
|
|
1400
1805
|
"aria-label": ariaLabel,
|
|
1401
1806
|
children: [
|
|
1402
|
-
items.length === 0 && /* @__PURE__ */
|
|
1807
|
+
items.length === 0 && /* @__PURE__ */ jsx12("span", {
|
|
1403
1808
|
children: "No data"
|
|
1404
1809
|
}),
|
|
1405
|
-
items.length > 0 && /* @__PURE__ */
|
|
1810
|
+
items.length > 0 && /* @__PURE__ */ jsxs8(Fragment4, {
|
|
1406
1811
|
children: [
|
|
1407
|
-
/* @__PURE__ */
|
|
1812
|
+
/* @__PURE__ */ jsxs8("p", {
|
|
1408
1813
|
className: "altertable-scatter-axis-title",
|
|
1409
1814
|
children: [
|
|
1410
1815
|
yLabel,
|
|
1411
1816
|
yUnit && ` (${yUnit})`
|
|
1412
1817
|
]
|
|
1413
1818
|
}),
|
|
1414
|
-
/* @__PURE__ */
|
|
1819
|
+
/* @__PURE__ */ jsx12("div", {
|
|
1415
1820
|
className: "altertable-scatter-axes",
|
|
1416
|
-
children: /* @__PURE__ */
|
|
1821
|
+
children: /* @__PURE__ */ jsxs8("div", {
|
|
1417
1822
|
className: "altertable-scatter-plot",
|
|
1418
1823
|
children: [
|
|
1419
|
-
yScale.ticks.map((value, index) => /* @__PURE__ */
|
|
1824
|
+
yScale.ticks.map((value, index) => /* @__PURE__ */ jsx12("div", {
|
|
1420
1825
|
className: "altertable-scatter-gridline",
|
|
1421
1826
|
style: { bottom: `${index * 50}%` },
|
|
1422
1827
|
"aria-hidden": "true",
|
|
1423
|
-
children: /* @__PURE__ */
|
|
1828
|
+
children: /* @__PURE__ */ jsx12("span", {
|
|
1424
1829
|
title: formatY(value),
|
|
1425
1830
|
children: formatY(value)
|
|
1426
1831
|
})
|
|
1427
1832
|
}, index)),
|
|
1428
|
-
xScale.ticks.map((value, index) => /* @__PURE__ */
|
|
1833
|
+
xScale.ticks.map((value, index) => /* @__PURE__ */ jsx12("span", {
|
|
1429
1834
|
className: "altertable-scatter-x-tick",
|
|
1430
1835
|
"data-edge": index,
|
|
1431
1836
|
style: { left: `${index * 50}%` },
|
|
@@ -1433,14 +1838,14 @@ function ScatterChart({
|
|
|
1433
1838
|
title: formatX(value),
|
|
1434
1839
|
children: formatX(value)
|
|
1435
1840
|
}, index)),
|
|
1436
|
-
items.map((item) => /* @__PURE__ */
|
|
1841
|
+
items.map((item) => /* @__PURE__ */ jsx12(Tooltip2, {
|
|
1437
1842
|
variant: "chart",
|
|
1438
|
-
content: /* @__PURE__ */
|
|
1843
|
+
content: /* @__PURE__ */ jsxs8(Fragment4, {
|
|
1439
1844
|
children: [
|
|
1440
|
-
/* @__PURE__ */
|
|
1845
|
+
/* @__PURE__ */ jsx12("strong", {
|
|
1441
1846
|
children: item.label
|
|
1442
1847
|
}),
|
|
1443
|
-
/* @__PURE__ */
|
|
1848
|
+
/* @__PURE__ */ jsxs8("span", {
|
|
1444
1849
|
children: [
|
|
1445
1850
|
xLabel,
|
|
1446
1851
|
": ",
|
|
@@ -1449,7 +1854,7 @@ function ScatterChart({
|
|
|
1449
1854
|
xUnit
|
|
1450
1855
|
]
|
|
1451
1856
|
}),
|
|
1452
|
-
/* @__PURE__ */
|
|
1857
|
+
/* @__PURE__ */ jsxs8("span", {
|
|
1453
1858
|
children: [
|
|
1454
1859
|
yLabel,
|
|
1455
1860
|
": ",
|
|
@@ -1465,20 +1870,20 @@ function ScatterChart({
|
|
|
1465
1870
|
left: `${xScale.position(item.x)}%`,
|
|
1466
1871
|
bottom: `${yScale.position(item.y)}%`
|
|
1467
1872
|
},
|
|
1468
|
-
children: /* @__PURE__ */
|
|
1873
|
+
children: /* @__PURE__ */ jsx12("button", {
|
|
1469
1874
|
"data-atbl-focus": "ring",
|
|
1470
|
-
"data-atbl-control": "
|
|
1875
|
+
"data-atbl-control": "default",
|
|
1471
1876
|
type: "button",
|
|
1472
1877
|
tabIndex: -1,
|
|
1473
1878
|
className: "altertable-scatter-point",
|
|
1474
1879
|
"aria-label": `${item.label}: ${xLabel} ${formatX(item.x)} ${xUnit}, ${yLabel} ${formatY(item.y)} ${yUnit}`,
|
|
1475
|
-
children: /* @__PURE__ */
|
|
1880
|
+
children: /* @__PURE__ */ jsx12("span", {})
|
|
1476
1881
|
})
|
|
1477
1882
|
}, item.id))
|
|
1478
1883
|
]
|
|
1479
1884
|
})
|
|
1480
1885
|
}),
|
|
1481
|
-
/* @__PURE__ */
|
|
1886
|
+
/* @__PURE__ */ jsxs8("p", {
|
|
1482
1887
|
className: "altertable-scatter-axis-title altertable-scatter-x-title",
|
|
1483
1888
|
children: [
|
|
1484
1889
|
xLabel,
|
|
@@ -1491,8 +1896,9 @@ function ScatterChart({
|
|
|
1491
1896
|
});
|
|
1492
1897
|
}
|
|
1493
1898
|
// src/react/ui/BarChart.tsx
|
|
1494
|
-
import { useLayoutEffect as useLayoutEffect2, useRef as useRef2 } from "react";
|
|
1495
|
-
import {
|
|
1899
|
+
import { useLayoutEffect as useLayoutEffect2, useRef as useRef2, useState as useState4 } from "react";
|
|
1900
|
+
import { BarChart as RechartsBarChart, Cell } from "recharts";
|
|
1901
|
+
import { jsx as jsx13, jsxs as jsxs9, Fragment as Fragment5 } from "react/jsx-runtime";
|
|
1496
1902
|
function BarChart({
|
|
1497
1903
|
items,
|
|
1498
1904
|
unit,
|
|
@@ -1500,6 +1906,7 @@ function BarChart({
|
|
|
1500
1906
|
formatValue = (value) => new Intl.NumberFormat().format(value)
|
|
1501
1907
|
}) {
|
|
1502
1908
|
validateChartItems("bar", items);
|
|
1909
|
+
const [activeId, setActiveId] = useState4();
|
|
1503
1910
|
const scrollport = useRef2(null);
|
|
1504
1911
|
useLayoutEffect2(() => {
|
|
1505
1912
|
const element = scrollport.current;
|
|
@@ -1507,59 +1914,1152 @@ function BarChart({
|
|
|
1507
1914
|
element.scrollLeft = element.scrollWidth - element.clientWidth;
|
|
1508
1915
|
}, [items]);
|
|
1509
1916
|
const maximum = items.reduce((max, item) => Math.max(max, item.value), 1);
|
|
1510
|
-
return /* @__PURE__ */
|
|
1917
|
+
return /* @__PURE__ */ jsxs9("section", {
|
|
1511
1918
|
className: "altertable-selectable-bars",
|
|
1512
1919
|
"aria-label": ariaLabel,
|
|
1513
1920
|
children: [
|
|
1514
|
-
items.length === 0 && /* @__PURE__ */
|
|
1921
|
+
items.length === 0 && /* @__PURE__ */ jsx13("span", {
|
|
1515
1922
|
children: "No data"
|
|
1516
1923
|
}),
|
|
1517
|
-
/* @__PURE__ */
|
|
1924
|
+
/* @__PURE__ */ jsx13("div", {
|
|
1518
1925
|
className: "altertable-selectable-bars-scroll",
|
|
1519
1926
|
ref: scrollport,
|
|
1520
|
-
children:
|
|
1521
|
-
|
|
1522
|
-
|
|
1927
|
+
children: /* @__PURE__ */ jsxs9("div", {
|
|
1928
|
+
className: "altertable-selectable-bars-plot",
|
|
1929
|
+
style: { minWidth: items.length * 45 },
|
|
1930
|
+
children: [
|
|
1931
|
+
/* @__PURE__ */ jsx13("div", {
|
|
1932
|
+
className: "altertable-chart-canvas",
|
|
1933
|
+
"aria-hidden": "true",
|
|
1934
|
+
children: /* @__PURE__ */ jsxs9(RechartsBarChart, {
|
|
1935
|
+
data: items.map((item) => ({
|
|
1936
|
+
...item,
|
|
1937
|
+
plotValue: item.value / maximum
|
|
1938
|
+
})),
|
|
1939
|
+
title: ariaLabel,
|
|
1940
|
+
responsive: true,
|
|
1941
|
+
width: "100%",
|
|
1942
|
+
height: 220,
|
|
1943
|
+
margin: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
1944
|
+
accessibilityLayer: false,
|
|
1945
|
+
tabIndex: -1,
|
|
1946
|
+
barCategoryGap: "24%",
|
|
1947
|
+
children: [
|
|
1948
|
+
/* @__PURE__ */ jsx13(ChartXAxis, {
|
|
1949
|
+
hide: true,
|
|
1950
|
+
dataKey: "id",
|
|
1951
|
+
scale: "band"
|
|
1952
|
+
}),
|
|
1953
|
+
/* @__PURE__ */ jsx13(ChartYAxis, {
|
|
1954
|
+
hide: true,
|
|
1955
|
+
domain: [0, 1],
|
|
1956
|
+
allowDataOverflow: true
|
|
1957
|
+
}),
|
|
1958
|
+
/* @__PURE__ */ jsx13(ChartBar, {
|
|
1959
|
+
dataKey: "plotValue",
|
|
1960
|
+
minPointSize: (value) => (value ?? 0) > 0 ? 7 : 0,
|
|
1961
|
+
children: items.map((item) => /* @__PURE__ */ jsx13(Cell, {
|
|
1962
|
+
fill: activeId === item.id ? "var(--atbl-accent)" : chartBarFill
|
|
1963
|
+
}, item.id))
|
|
1964
|
+
})
|
|
1965
|
+
]
|
|
1966
|
+
})
|
|
1967
|
+
}),
|
|
1968
|
+
/* @__PURE__ */ jsx13("div", {
|
|
1969
|
+
className: "altertable-selectable-bars-items",
|
|
1970
|
+
children: items.map((item) => /* @__PURE__ */ jsx13(Tooltip2, {
|
|
1971
|
+
variant: "chart",
|
|
1972
|
+
onOpenChange: (open) => setActiveId((current) => open ? item.id : current === item.id ? undefined : current),
|
|
1973
|
+
content: /* @__PURE__ */ jsxs9(Fragment5, {
|
|
1974
|
+
children: [
|
|
1975
|
+
/* @__PURE__ */ jsx13("strong", {
|
|
1976
|
+
children: item.label
|
|
1977
|
+
}),
|
|
1978
|
+
/* @__PURE__ */ jsxs9("span", {
|
|
1979
|
+
children: [
|
|
1980
|
+
formatValue(item.value),
|
|
1981
|
+
" ",
|
|
1982
|
+
unit
|
|
1983
|
+
]
|
|
1984
|
+
})
|
|
1985
|
+
]
|
|
1986
|
+
}),
|
|
1987
|
+
className: "altertable-chart-column",
|
|
1988
|
+
children: /* @__PURE__ */ jsxs9("button", {
|
|
1989
|
+
"data-atbl-focus": "ring",
|
|
1990
|
+
"data-atbl-control": "default",
|
|
1991
|
+
type: "button",
|
|
1992
|
+
tabIndex: -1,
|
|
1993
|
+
className: "altertable-selectable-bars-item",
|
|
1994
|
+
"aria-label": `${item.label}: ${formatValue(item.value)} ${unit}`,
|
|
1995
|
+
children: [
|
|
1996
|
+
/* @__PURE__ */ jsx13("span", {
|
|
1997
|
+
className: "altertable-selectable-bars-area"
|
|
1998
|
+
}),
|
|
1999
|
+
/* @__PURE__ */ jsx13("span", {
|
|
2000
|
+
className: "altertable-selectable-bars-label",
|
|
2001
|
+
children: item.label
|
|
2002
|
+
})
|
|
2003
|
+
]
|
|
2004
|
+
})
|
|
2005
|
+
}, item.id))
|
|
2006
|
+
})
|
|
2007
|
+
]
|
|
2008
|
+
})
|
|
2009
|
+
})
|
|
2010
|
+
]
|
|
2011
|
+
});
|
|
2012
|
+
}
|
|
2013
|
+
// src/react/ui/RadioGroup.tsx
|
|
2014
|
+
import {
|
|
2015
|
+
RadioGroup as AriaRadioGroup,
|
|
2016
|
+
Radio as AriaRadio,
|
|
2017
|
+
Label
|
|
2018
|
+
} from "react-aria-components";
|
|
2019
|
+
import { jsx as jsx14, jsxs as jsxs10, Fragment as Fragment6 } from "react/jsx-runtime";
|
|
2020
|
+
function RadioGroup({
|
|
2021
|
+
label,
|
|
2022
|
+
className,
|
|
2023
|
+
children,
|
|
2024
|
+
...props
|
|
2025
|
+
}) {
|
|
2026
|
+
return /* @__PURE__ */ jsxs10(AriaRadioGroup, {
|
|
2027
|
+
...props,
|
|
2028
|
+
className: classNames("altertable-radio-group", className),
|
|
2029
|
+
children: [
|
|
2030
|
+
/* @__PURE__ */ jsx14(Label, {
|
|
2031
|
+
className: "altertable-control-label",
|
|
2032
|
+
children: label
|
|
2033
|
+
}),
|
|
2034
|
+
children
|
|
2035
|
+
]
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2038
|
+
function Radio({ className, children, ...props }) {
|
|
2039
|
+
return /* @__PURE__ */ jsx14(AriaRadio, {
|
|
2040
|
+
...props,
|
|
2041
|
+
"data-atbl-control": "action",
|
|
2042
|
+
"data-atbl-focus": "ring",
|
|
2043
|
+
className: classNames("altertable-radio", className),
|
|
2044
|
+
children: (state) => /* @__PURE__ */ jsxs10(Fragment6, {
|
|
2045
|
+
children: [
|
|
2046
|
+
/* @__PURE__ */ jsx14("span", {
|
|
2047
|
+
className: "altertable-radio-mark",
|
|
2048
|
+
"aria-hidden": "true"
|
|
2049
|
+
}),
|
|
2050
|
+
typeof children === "function" ? children(state) : children
|
|
2051
|
+
]
|
|
2052
|
+
})
|
|
2053
|
+
});
|
|
2054
|
+
}
|
|
2055
|
+
function SegmentedControl({
|
|
2056
|
+
options,
|
|
2057
|
+
className,
|
|
2058
|
+
...props
|
|
2059
|
+
}) {
|
|
2060
|
+
validateChoiceOptions(options);
|
|
2061
|
+
return /* @__PURE__ */ jsx14(RadioGroup, {
|
|
2062
|
+
...props,
|
|
2063
|
+
orientation: "horizontal",
|
|
2064
|
+
className: classNames("altertable-segmented-control", className),
|
|
2065
|
+
children: /* @__PURE__ */ jsx14("div", {
|
|
2066
|
+
className: "altertable-segmented-options",
|
|
2067
|
+
children: options.map((option) => /* @__PURE__ */ jsx14(Radio, {
|
|
2068
|
+
value: option.id,
|
|
2069
|
+
"data-atbl-internal-surface": "option",
|
|
2070
|
+
children: option.label
|
|
2071
|
+
}, option.id))
|
|
2072
|
+
})
|
|
2073
|
+
});
|
|
2074
|
+
}
|
|
2075
|
+
// src/react/ui/CheckboxGroup.tsx
|
|
2076
|
+
import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2077
|
+
function CheckboxGroup({
|
|
2078
|
+
label,
|
|
2079
|
+
values,
|
|
2080
|
+
onChange,
|
|
2081
|
+
className,
|
|
2082
|
+
children,
|
|
2083
|
+
disabled,
|
|
2084
|
+
...props
|
|
2085
|
+
}) {
|
|
2086
|
+
invariant(new Set(values).size === values.length, "Checkbox group values must be unique.");
|
|
2087
|
+
return /* @__PURE__ */ jsxs11("fieldset", {
|
|
2088
|
+
...props,
|
|
2089
|
+
disabled,
|
|
2090
|
+
className: classNames("altertable-checkbox-group", className),
|
|
2091
|
+
children: [
|
|
2092
|
+
/* @__PURE__ */ jsx15("legend", {
|
|
2093
|
+
children: label
|
|
2094
|
+
}),
|
|
2095
|
+
/* @__PURE__ */ jsx15(CheckboxGroupContext, {
|
|
2096
|
+
value: {
|
|
2097
|
+
values,
|
|
2098
|
+
disabled,
|
|
2099
|
+
onChange(value, checked) {
|
|
2100
|
+
onChange(checked ? [...values.filter((item) => item !== value), value] : values.filter((item) => item !== value));
|
|
2101
|
+
}
|
|
2102
|
+
},
|
|
2103
|
+
children
|
|
2104
|
+
})
|
|
2105
|
+
]
|
|
2106
|
+
});
|
|
2107
|
+
}
|
|
2108
|
+
// src/react/ui/ComposedChart.tsx
|
|
2109
|
+
import { useState as useState5 } from "react";
|
|
2110
|
+
|
|
2111
|
+
// src/react/ui/ComposedChartLegend.tsx
|
|
2112
|
+
import { Legend } from "recharts";
|
|
2113
|
+
import { jsx as jsx16, jsxs as jsxs12, Fragment as Fragment7 } from "react/jsx-runtime";
|
|
2114
|
+
var legendMarkerKinds = {
|
|
2115
|
+
square: "area",
|
|
2116
|
+
rect: "bar",
|
|
2117
|
+
line: "line",
|
|
2118
|
+
plainline: "line"
|
|
2119
|
+
};
|
|
2120
|
+
function LegendContent({
|
|
2121
|
+
payload = [],
|
|
2122
|
+
maxVisibleItems,
|
|
2123
|
+
formatter,
|
|
2124
|
+
layout,
|
|
2125
|
+
align,
|
|
2126
|
+
labelStyle,
|
|
2127
|
+
iconType,
|
|
2128
|
+
iconSize = 8,
|
|
2129
|
+
inactiveColor = "var(--atbl-muted)",
|
|
2130
|
+
position,
|
|
2131
|
+
verticalAlign,
|
|
2132
|
+
onClick,
|
|
2133
|
+
onMouseEnter,
|
|
2134
|
+
onMouseLeave,
|
|
2135
|
+
"aria-label": ariaLabel
|
|
2136
|
+
}) {
|
|
2137
|
+
if (!payload.some((entry) => entry.type !== "none"))
|
|
2138
|
+
return null;
|
|
2139
|
+
const vertical = layout === "vertical" || layout === "auto" && (position === "left" || position === "right");
|
|
2140
|
+
return /* @__PURE__ */ jsx16(ChartLegend, {
|
|
2141
|
+
"aria-label": ariaLabel,
|
|
2142
|
+
className: position === "top" || position == null && verticalAlign === "top" ? "altertable-composed-chart-legend-above" : position === "bottom" || position == null && verticalAlign !== "middle" && !vertical ? "altertable-composed-chart-legend-below" : undefined,
|
|
2143
|
+
maxVisibleItems,
|
|
2144
|
+
layout: vertical ? "vertical" : "horizontal",
|
|
2145
|
+
align: align === "right" ? "end" : align === "left" ? "start" : "center",
|
|
2146
|
+
children: payload.map((entry, index) => {
|
|
2147
|
+
if (entry.type === "none")
|
|
2148
|
+
return null;
|
|
2149
|
+
const type = iconType ?? entry.type;
|
|
2150
|
+
const kind = iconType ? legendMarkerKinds[type ?? "none"] ?? "point" : "square";
|
|
2151
|
+
const formatLabel = entry.formatter ?? formatter;
|
|
2152
|
+
const label = formatLabel ? formatLabel(entry.value, entry, index) : entry.value;
|
|
2153
|
+
const content = /* @__PURE__ */ jsxs12(Fragment7, {
|
|
2154
|
+
children: [
|
|
2155
|
+
type !== "none" && (entry.legendIcon ?? /* @__PURE__ */ jsx16(ChartLegend.Marker, {
|
|
2156
|
+
kind,
|
|
2157
|
+
color: entry.inactive ? inactiveColor : entry.color,
|
|
2158
|
+
style: {
|
|
2159
|
+
width: iconSize,
|
|
2160
|
+
height: kind === "square" ? iconSize : iconSize * 0.6
|
|
2161
|
+
}
|
|
2162
|
+
})),
|
|
2163
|
+
/* @__PURE__ */ jsx16(ChartLegend.Label, {
|
|
2164
|
+
style: labelStyle,
|
|
2165
|
+
children: label
|
|
2166
|
+
})
|
|
2167
|
+
]
|
|
2168
|
+
});
|
|
2169
|
+
return /* @__PURE__ */ jsx16(ChartLegend.Item, {
|
|
2170
|
+
inactive: entry.inactive,
|
|
2171
|
+
onMouseEnter: onMouseEnter ? (event) => onMouseEnter(entry, index, event) : undefined,
|
|
2172
|
+
onMouseLeave: onMouseLeave ? (event) => onMouseLeave(entry, index, event) : undefined,
|
|
2173
|
+
children: onClick ? /* @__PURE__ */ jsx16("button", {
|
|
2174
|
+
type: "button",
|
|
2175
|
+
"data-atbl-control": "action",
|
|
2176
|
+
"data-atbl-focus": "ring",
|
|
2177
|
+
className: "altertable-chart-legend-action",
|
|
2178
|
+
onClick: (event) => onClick(entry, index, event),
|
|
2179
|
+
children: content
|
|
2180
|
+
}) : content
|
|
2181
|
+
}, index);
|
|
2182
|
+
})
|
|
2183
|
+
});
|
|
2184
|
+
}
|
|
2185
|
+
function ComposedChartLegend({
|
|
2186
|
+
maxVisibleItems,
|
|
2187
|
+
...props
|
|
2188
|
+
}) {
|
|
2189
|
+
return /* @__PURE__ */ jsx16(Legend, {
|
|
2190
|
+
iconSize: 8,
|
|
2191
|
+
inactiveColor: "var(--atbl-muted)",
|
|
2192
|
+
content: /* @__PURE__ */ jsx16(LegendContent, {
|
|
2193
|
+
maxVisibleItems
|
|
2194
|
+
}),
|
|
2195
|
+
...props
|
|
2196
|
+
});
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
// src/react/ui/ComposedChart.tsx
|
|
2200
|
+
import {
|
|
2201
|
+
ComposedChart as RechartsComposedChart,
|
|
2202
|
+
ZAxis,
|
|
2203
|
+
ReferenceLine as ReferenceLine2,
|
|
2204
|
+
ReferenceArea,
|
|
2205
|
+
ReferenceDot,
|
|
2206
|
+
Brush,
|
|
2207
|
+
Cell as Cell2,
|
|
2208
|
+
Label as Label2,
|
|
2209
|
+
LabelList,
|
|
2210
|
+
ErrorBar
|
|
2211
|
+
} from "recharts";
|
|
2212
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
2213
|
+
function ComposedChartRoot({
|
|
2214
|
+
data,
|
|
2215
|
+
ariaLabel,
|
|
2216
|
+
width = "100%",
|
|
2217
|
+
height = 300,
|
|
2218
|
+
responsive = true,
|
|
2219
|
+
className,
|
|
2220
|
+
...props
|
|
2221
|
+
}) {
|
|
2222
|
+
const [pointerFocus, setPointerFocus] = useState5(false);
|
|
2223
|
+
return /* @__PURE__ */ jsx17("section", {
|
|
2224
|
+
className: ["altertable-composed-chart", className].filter(Boolean).join(" "),
|
|
2225
|
+
"aria-label": ariaLabel,
|
|
2226
|
+
"data-atbl-internal-pointer-focus": pointerFocus || undefined,
|
|
2227
|
+
onPointerDownCapture: () => setPointerFocus(true),
|
|
2228
|
+
onKeyDownCapture: () => setPointerFocus(false),
|
|
2229
|
+
onBlurCapture: (event) => {
|
|
2230
|
+
if (!event.currentTarget.contains(event.relatedTarget))
|
|
2231
|
+
setPointerFocus(false);
|
|
2232
|
+
},
|
|
2233
|
+
children: /* @__PURE__ */ jsx17(RechartsComposedChart, {
|
|
2234
|
+
"data-atbl-focus": "inset",
|
|
2235
|
+
...props,
|
|
2236
|
+
data,
|
|
2237
|
+
width,
|
|
2238
|
+
height,
|
|
2239
|
+
responsive,
|
|
2240
|
+
title: ariaLabel
|
|
2241
|
+
})
|
|
2242
|
+
});
|
|
2243
|
+
}
|
|
2244
|
+
var ComposedChart = Object.assign(ComposedChartRoot, {
|
|
2245
|
+
Dot: ChartDot,
|
|
2246
|
+
Area: ChartArea,
|
|
2247
|
+
Bar: ChartBar,
|
|
2248
|
+
Line: ChartLine,
|
|
2249
|
+
Scatter: ChartScatter,
|
|
2250
|
+
XAxis: ChartXAxis,
|
|
2251
|
+
YAxis: ChartYAxis,
|
|
2252
|
+
ZAxis,
|
|
2253
|
+
CartesianGrid: ChartGrid,
|
|
2254
|
+
Tooltip: ChartTooltip,
|
|
2255
|
+
Legend: ComposedChartLegend,
|
|
2256
|
+
ReferenceLine: ReferenceLine2,
|
|
2257
|
+
ReferenceArea,
|
|
2258
|
+
ReferenceDot,
|
|
2259
|
+
Brush,
|
|
2260
|
+
Cell: Cell2,
|
|
2261
|
+
Label: Label2,
|
|
2262
|
+
LabelList,
|
|
2263
|
+
ErrorBar
|
|
2264
|
+
});
|
|
2265
|
+
// src/react/ui/FunnelChart.tsx
|
|
2266
|
+
import { Fragment as Fragment8, useEffect, useRef as useRef3, useState as useState6 } from "react";
|
|
2267
|
+
|
|
2268
|
+
// src/react/ui/useSvgId.ts
|
|
2269
|
+
import { useId as useId2 } from "react";
|
|
2270
|
+
function useSvgId() {
|
|
2271
|
+
const id = useId2();
|
|
2272
|
+
return `svg-${Array.from(id, (character) => /[a-zA-Z0-9-]/.test(character) ? character : `_${character.codePointAt(0).toString(16)}_`).join("")}`;
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
// src/react/ui/FunnelChart.tsx
|
|
2276
|
+
import { jsx as jsx18, jsxs as jsxs13, Fragment as Fragment9 } from "react/jsx-runtime";
|
|
2277
|
+
function FunnelChart({
|
|
2278
|
+
steps,
|
|
2279
|
+
series,
|
|
2280
|
+
unit,
|
|
2281
|
+
ariaLabel,
|
|
2282
|
+
className,
|
|
2283
|
+
formatValue = formatNumber2
|
|
2284
|
+
}) {
|
|
2285
|
+
const chartId = useSvgId();
|
|
2286
|
+
const [highlighted, setHighlighted] = useState6(null);
|
|
2287
|
+
const clearTimer = useRef3(null);
|
|
2288
|
+
function cancelClear() {
|
|
2289
|
+
if (clearTimer.current !== null)
|
|
2290
|
+
clearTimeout(clearTimer.current);
|
|
2291
|
+
clearTimer.current = null;
|
|
2292
|
+
}
|
|
2293
|
+
useEffect(() => cancelClear, []);
|
|
2294
|
+
function highlight(id, conversion, step) {
|
|
2295
|
+
cancelClear();
|
|
2296
|
+
setHighlighted({ id, conversion, step });
|
|
2297
|
+
}
|
|
2298
|
+
function clearHighlight() {
|
|
2299
|
+
cancelClear();
|
|
2300
|
+
setHighlighted(null);
|
|
2301
|
+
}
|
|
2302
|
+
function scheduleClear() {
|
|
2303
|
+
cancelClear();
|
|
2304
|
+
clearTimer.current = setTimeout(() => setHighlighted(null), 100);
|
|
2305
|
+
}
|
|
2306
|
+
for (const [kind, items] of [
|
|
2307
|
+
["funnel step", steps],
|
|
2308
|
+
["funnel series", series]
|
|
2309
|
+
]) {
|
|
2310
|
+
const ids = new Set;
|
|
2311
|
+
for (const item of items) {
|
|
2312
|
+
invariant(item.id.trim().length > 0 && !ids.has(item.id), `${kind} chart IDs must be nonblank and unique.`);
|
|
2313
|
+
ids.add(item.id);
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
for (const item of series) {
|
|
2317
|
+
invariant(item.values.length === steps.length, "funnel chart counts must align with steps.");
|
|
2318
|
+
item.values.forEach((value, index) => {
|
|
2319
|
+
invariant(Number.isFinite(value) && value >= 0, "funnel chart counts must be finite and nonnegative.");
|
|
2320
|
+
invariant(index === 0 || value <= item.values[index - 1], "funnel chart counts must be nonincreasing.");
|
|
2321
|
+
});
|
|
2322
|
+
}
|
|
2323
|
+
function reading(item, index) {
|
|
2324
|
+
const first = item.values[0] ?? 0;
|
|
2325
|
+
const current = item.values[index];
|
|
2326
|
+
const previous = item.values[Math.max(0, index - 1)];
|
|
2327
|
+
return {
|
|
2328
|
+
current,
|
|
2329
|
+
previous,
|
|
2330
|
+
dropped: previous - current,
|
|
2331
|
+
conversion: first > 0 ? current / first : null,
|
|
2332
|
+
dropOff: first > 0 ? (previous - current) / first : null
|
|
2333
|
+
};
|
|
2334
|
+
}
|
|
2335
|
+
const data = steps.map((step, index) => ({
|
|
2336
|
+
label: step.label,
|
|
2337
|
+
index,
|
|
2338
|
+
...Object.fromEntries(series.flatMap((item, seriesIndex) => {
|
|
2339
|
+
const value = reading(item, index);
|
|
2340
|
+
return [
|
|
2341
|
+
[`conversion${seriesIndex}`, (value.conversion ?? 0) * 100],
|
|
2342
|
+
[
|
|
2343
|
+
`drop${seriesIndex}`,
|
|
2344
|
+
(value.dropOff ?? (index === 0 ? 1 : 0)) * 100
|
|
2345
|
+
]
|
|
2346
|
+
];
|
|
2347
|
+
}))
|
|
2348
|
+
}));
|
|
2349
|
+
return /* @__PURE__ */ jsx18("section", {
|
|
2350
|
+
className: classNames("altertable-funnel-chart", className),
|
|
2351
|
+
"aria-label": ariaLabel,
|
|
2352
|
+
onKeyDownCapture: clearHighlight,
|
|
2353
|
+
children: steps.length === 0 || series.length === 0 ? /* @__PURE__ */ jsx18("span", {
|
|
2354
|
+
children: "No data"
|
|
2355
|
+
}) : /* @__PURE__ */ jsxs13(Fragment9, {
|
|
2356
|
+
children: [
|
|
2357
|
+
/* @__PURE__ */ jsx18("div", {
|
|
2358
|
+
className: "altertable-funnel-summary",
|
|
2359
|
+
children: series.map((item, index) => /* @__PURE__ */ jsxs13("div", {
|
|
1523
2360
|
children: [
|
|
1524
|
-
/* @__PURE__ */
|
|
1525
|
-
children: item.label
|
|
1526
|
-
}),
|
|
1527
|
-
/* @__PURE__ */ jsxs7("span", {
|
|
2361
|
+
/* @__PURE__ */ jsxs13("span", {
|
|
1528
2362
|
children: [
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
2363
|
+
/* @__PURE__ */ jsx18(ChartLegend.Marker, {
|
|
2364
|
+
kind: "square",
|
|
2365
|
+
color: `var(--atbl-chart-${index % 8 + 1})`
|
|
2366
|
+
}),
|
|
2367
|
+
item.label
|
|
1532
2368
|
]
|
|
2369
|
+
}),
|
|
2370
|
+
/* @__PURE__ */ jsx18("strong", {
|
|
2371
|
+
children: formatPercent2(reading(item, steps.length - 1).conversion)
|
|
1533
2372
|
})
|
|
1534
2373
|
]
|
|
1535
|
-
})
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
2374
|
+
}, item.id))
|
|
2375
|
+
}),
|
|
2376
|
+
/* @__PURE__ */ jsxs13("p", {
|
|
2377
|
+
className: "altertable-analytics-note",
|
|
2378
|
+
children: [
|
|
2379
|
+
steps[0].label,
|
|
2380
|
+
" → ",
|
|
2381
|
+
steps.at(-1).label
|
|
2382
|
+
]
|
|
2383
|
+
}),
|
|
2384
|
+
/* @__PURE__ */ jsx18("div", {
|
|
2385
|
+
className: "altertable-funnel-scroll",
|
|
2386
|
+
children: /* @__PURE__ */ jsx18("div", {
|
|
2387
|
+
style: {
|
|
2388
|
+
minWidth: steps.length * Math.max(96, series.length * 40)
|
|
2389
|
+
},
|
|
2390
|
+
children: /* @__PURE__ */ jsxs13(ComposedChart, {
|
|
2391
|
+
data,
|
|
2392
|
+
ariaLabel: `${ariaLabel} plot`,
|
|
2393
|
+
height: 300,
|
|
2394
|
+
barCategoryGap: "4%",
|
|
2395
|
+
barGap: 1,
|
|
2396
|
+
onMouseLeave: clearHighlight,
|
|
2397
|
+
children: [
|
|
2398
|
+
/* @__PURE__ */ jsx18(ComposedChart.CartesianGrid, {}),
|
|
2399
|
+
/* @__PURE__ */ jsx18(ComposedChart.XAxis, {
|
|
2400
|
+
dataKey: "label",
|
|
2401
|
+
interval: 0,
|
|
2402
|
+
height: 48
|
|
2403
|
+
}),
|
|
2404
|
+
/* @__PURE__ */ jsx18(ComposedChart.YAxis, {
|
|
2405
|
+
domain: [0, 100],
|
|
2406
|
+
width: 42,
|
|
2407
|
+
tickFormatter: (value) => `${value}%`
|
|
2408
|
+
}),
|
|
2409
|
+
/* @__PURE__ */ jsx18("defs", {
|
|
2410
|
+
children: series.map((item, index) => /* @__PURE__ */ jsxs13(Fragment8, {
|
|
2411
|
+
children: [
|
|
2412
|
+
/* @__PURE__ */ jsxs13("linearGradient", {
|
|
2413
|
+
id: `${chartId}-gradient-${index}`,
|
|
2414
|
+
x1: "0",
|
|
2415
|
+
x2: "0",
|
|
2416
|
+
y1: "1",
|
|
2417
|
+
y2: "0",
|
|
2418
|
+
children: [
|
|
2419
|
+
/* @__PURE__ */ jsx18("stop", {
|
|
2420
|
+
offset: "0%",
|
|
2421
|
+
stopColor: `var(--atbl-chart-${index % 8 + 1})`,
|
|
2422
|
+
stopOpacity: 0.5
|
|
2423
|
+
}),
|
|
2424
|
+
/* @__PURE__ */ jsx18("stop", {
|
|
2425
|
+
offset: "100%",
|
|
2426
|
+
stopColor: `var(--atbl-chart-${index % 8 + 1})`,
|
|
2427
|
+
stopOpacity: 0.7
|
|
2428
|
+
})
|
|
2429
|
+
]
|
|
2430
|
+
}),
|
|
2431
|
+
/* @__PURE__ */ jsxs13("pattern", {
|
|
2432
|
+
id: `${chartId}-hatch-${index}`,
|
|
2433
|
+
patternUnits: "userSpaceOnUse",
|
|
2434
|
+
width: "6",
|
|
2435
|
+
height: "6",
|
|
2436
|
+
patternTransform: "rotate(45)",
|
|
2437
|
+
children: [
|
|
2438
|
+
/* @__PURE__ */ jsx18("rect", {
|
|
2439
|
+
width: "8",
|
|
2440
|
+
height: "8",
|
|
2441
|
+
fill: `var(--atbl-chart-${index % 8 + 1})`,
|
|
2442
|
+
fillOpacity: 0.16
|
|
2443
|
+
}),
|
|
2444
|
+
/* @__PURE__ */ jsx18("line", {
|
|
2445
|
+
x1: "0",
|
|
2446
|
+
y1: "0",
|
|
2447
|
+
x2: "0",
|
|
2448
|
+
y2: "8",
|
|
2449
|
+
stroke: `var(--atbl-chart-${index % 8 + 1})`,
|
|
2450
|
+
strokeOpacity: 0.4,
|
|
2451
|
+
strokeWidth: "2"
|
|
2452
|
+
})
|
|
2453
|
+
]
|
|
2454
|
+
})
|
|
2455
|
+
]
|
|
2456
|
+
}, item.id))
|
|
2457
|
+
}),
|
|
2458
|
+
series.map((item, index) => /* @__PURE__ */ jsxs13(Fragment8, {
|
|
2459
|
+
children: [
|
|
2460
|
+
/* @__PURE__ */ jsx18(ComposedChart.Bar, {
|
|
2461
|
+
dataKey: `conversion${index}`,
|
|
2462
|
+
name: item.label,
|
|
2463
|
+
stackId: item.id,
|
|
2464
|
+
fill: `url(#${chartId}-gradient-${index})`,
|
|
2465
|
+
radius: 0,
|
|
2466
|
+
maxBarSize: undefined,
|
|
2467
|
+
activeBar: false,
|
|
2468
|
+
minPointSize: 0,
|
|
2469
|
+
opacity: !highlighted || highlighted.id === item.id ? 1 : 0.2,
|
|
2470
|
+
onMouseEnter: (bar) => highlight(item.id, true, bar.payload.index),
|
|
2471
|
+
onMouseLeave: scheduleClear,
|
|
2472
|
+
onClick: (bar) => highlight(item.id, true, bar.payload.index)
|
|
2473
|
+
}),
|
|
2474
|
+
/* @__PURE__ */ jsx18(ComposedChart.Bar, {
|
|
2475
|
+
dataKey: `drop${index}`,
|
|
2476
|
+
name: `${item.label} drop-off`,
|
|
2477
|
+
stackId: item.id,
|
|
2478
|
+
fill: `url(#${chartId}-hatch-${index})`,
|
|
2479
|
+
opacity: !highlighted || highlighted.id === item.id ? 1 : 0.2,
|
|
2480
|
+
onMouseEnter: (bar) => highlight(item.id, false, bar.payload.index),
|
|
2481
|
+
onMouseLeave: scheduleClear,
|
|
2482
|
+
onClick: (bar) => highlight(item.id, false, bar.payload.index),
|
|
2483
|
+
radius: 0,
|
|
2484
|
+
maxBarSize: undefined,
|
|
2485
|
+
activeBar: false
|
|
2486
|
+
})
|
|
2487
|
+
]
|
|
2488
|
+
}, item.id)),
|
|
2489
|
+
/* @__PURE__ */ jsx18(ComposedChart.Tooltip, {
|
|
2490
|
+
content: ({ active, payload }) => {
|
|
2491
|
+
const index = payload?.[0]?.payload.index;
|
|
2492
|
+
if (!active || typeof index !== "number")
|
|
2493
|
+
return null;
|
|
2494
|
+
const tooltipItems = series.filter((item) => !highlighted || highlighted.step !== index || item.id === highlighted.id).flatMap((item) => {
|
|
2495
|
+
const value = reading(item, index);
|
|
2496
|
+
const selected = highlighted?.step === index;
|
|
2497
|
+
const readings = selected ? [highlighted.conversion] : [true, false];
|
|
2498
|
+
const seriesIndex = series.indexOf(item);
|
|
2499
|
+
return readings.map((conversion) => ({
|
|
2500
|
+
name: item.label,
|
|
2501
|
+
graphicalItemId: `${item.id}-${conversion ? "conversion" : "drop-off"}`,
|
|
2502
|
+
dataKey: `${item.id}-${conversion ? "conversion" : "drop-off"}`,
|
|
2503
|
+
color: `var(--atbl-chart-${seriesIndex % 8 + 1})`,
|
|
2504
|
+
value: (conversion ? value.conversion : value.dropOff) ?? 0,
|
|
2505
|
+
formatter: () => /* @__PURE__ */ jsxs13(Fragment9, {
|
|
2506
|
+
children: [
|
|
2507
|
+
formatPercent2(conversion ? value.conversion : value.dropOff),
|
|
2508
|
+
" ",
|
|
2509
|
+
conversion ? "conversion" : "drop-off",
|
|
2510
|
+
/* @__PURE__ */ jsxs13("span", {
|
|
2511
|
+
className: "altertable-funnel-tooltip-detail",
|
|
2512
|
+
children: [
|
|
2513
|
+
formatValue(conversion ? value.current : value.dropped),
|
|
2514
|
+
" ",
|
|
2515
|
+
"of ",
|
|
2516
|
+
formatValue(value.previous),
|
|
2517
|
+
" ",
|
|
2518
|
+
unit
|
|
2519
|
+
]
|
|
2520
|
+
})
|
|
2521
|
+
]
|
|
2522
|
+
})
|
|
2523
|
+
}));
|
|
2524
|
+
});
|
|
2525
|
+
return /* @__PURE__ */ jsx18(ChartTooltipContent, {
|
|
2526
|
+
active: true,
|
|
2527
|
+
label: steps[index].label,
|
|
2528
|
+
payload: tooltipItems
|
|
2529
|
+
});
|
|
1551
2530
|
}
|
|
1552
2531
|
})
|
|
2532
|
+
]
|
|
2533
|
+
})
|
|
2534
|
+
})
|
|
2535
|
+
}),
|
|
2536
|
+
/* @__PURE__ */ jsx18("ul", {
|
|
2537
|
+
className: "altertable-sr-only",
|
|
2538
|
+
children: steps.map((step, index) => /* @__PURE__ */ jsxs13("li", {
|
|
2539
|
+
children: [
|
|
2540
|
+
step.label,
|
|
2541
|
+
series.map((item) => {
|
|
2542
|
+
const value = reading(item, index);
|
|
2543
|
+
return /* @__PURE__ */ jsxs13("span", {
|
|
2544
|
+
children: [
|
|
2545
|
+
" ",
|
|
2546
|
+
"· ",
|
|
2547
|
+
item.label,
|
|
2548
|
+
": ",
|
|
2549
|
+
formatValue(value.current),
|
|
2550
|
+
" ",
|
|
2551
|
+
unit,
|
|
2552
|
+
",",
|
|
2553
|
+
" ",
|
|
2554
|
+
formatPercent2(value.conversion),
|
|
2555
|
+
" conversion,",
|
|
2556
|
+
" ",
|
|
2557
|
+
formatValue(value.dropped),
|
|
2558
|
+
" dropped off"
|
|
2559
|
+
]
|
|
2560
|
+
}, item.id);
|
|
2561
|
+
})
|
|
2562
|
+
]
|
|
2563
|
+
}, step.id))
|
|
2564
|
+
})
|
|
2565
|
+
]
|
|
2566
|
+
})
|
|
2567
|
+
});
|
|
2568
|
+
}
|
|
2569
|
+
// src/react/ui/RetentionChart.tsx
|
|
2570
|
+
import { Fragment as Fragment10 } from "react";
|
|
2571
|
+
import { jsx as jsx19, jsxs as jsxs14, Fragment as Fragment11 } from "react/jsx-runtime";
|
|
2572
|
+
function RetentionChart({
|
|
2573
|
+
series,
|
|
2574
|
+
unit,
|
|
2575
|
+
ariaLabel,
|
|
2576
|
+
className,
|
|
2577
|
+
formatValue = formatNumber2
|
|
2578
|
+
}) {
|
|
2579
|
+
const ids = new Set;
|
|
2580
|
+
const offsets = new Map;
|
|
2581
|
+
for (const item of series) {
|
|
2582
|
+
invariant(item.id.trim().length > 0 && !ids.has(item.id), "retention series chart IDs must be nonblank and unique.");
|
|
2583
|
+
ids.add(item.id);
|
|
2584
|
+
invariant(Number.isFinite(item.cohortSize) && item.cohortSize >= 0, "retention chart counts must be finite and nonnegative.");
|
|
2585
|
+
const seen = new Set;
|
|
2586
|
+
for (const point of item.points) {
|
|
2587
|
+
invariant(Number.isSafeInteger(point.offset) && point.offset >= 0 && !seen.has(point.offset), "retention chart offsets must be unique nonnegative integers within each series.");
|
|
2588
|
+
seen.add(point.offset);
|
|
2589
|
+
invariant(point.rate === null || Number.isFinite(point.rate) && point.rate >= 0 && point.rate <= 1, "retention chart rates must be finite fractions between 0 and 1, or null.");
|
|
2590
|
+
invariant(point.rate === null === (point.retainedCount === null), "retention chart unobserved rates and counts must both be null.");
|
|
2591
|
+
if (point.retainedCount !== null) {
|
|
2592
|
+
invariant(Number.isFinite(point.retainedCount) && point.retainedCount >= 0, "retention chart counts must be finite and nonnegative.");
|
|
2593
|
+
invariant(point.retainedCount <= item.cohortSize, "retention chart retained counts cannot exceed cohort size.");
|
|
2594
|
+
}
|
|
2595
|
+
invariant(!offsets.has(point.offset) || offsets.get(point.offset) === point.label, "retention chart labels must agree for the same offset.");
|
|
2596
|
+
offsets.set(point.offset, point.label);
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
const data = [...offsets].sort(([a], [b]) => a - b).map(([offset, label]) => ({
|
|
2600
|
+
offset,
|
|
2601
|
+
label,
|
|
2602
|
+
...Object.fromEntries(series.flatMap((_, index) => [
|
|
2603
|
+
[`rate${index}`, null],
|
|
2604
|
+
[`tail${index}`, null]
|
|
2605
|
+
]))
|
|
2606
|
+
}));
|
|
2607
|
+
series.forEach((item, index) => {
|
|
2608
|
+
const points = [...item.points].sort((a, b) => a.offset - b.offset);
|
|
2609
|
+
points.forEach((point, pointIndex) => {
|
|
2610
|
+
const row = data.find((row) => row.offset === point.offset);
|
|
2611
|
+
row[point.incomplete ? `tail${index}` : `rate${index}`] = point.rate === null ? null : point.rate * 100;
|
|
2612
|
+
if (point.incomplete && pointIndex > 0) {
|
|
2613
|
+
const previous = points[pointIndex - 1];
|
|
2614
|
+
data.find((row) => row.offset === previous.offset)[`tail${index}`] = previous.rate === null ? null : previous.rate * 100;
|
|
2615
|
+
}
|
|
2616
|
+
});
|
|
2617
|
+
});
|
|
2618
|
+
return /* @__PURE__ */ jsx19("section", {
|
|
2619
|
+
className: classNames("altertable-retention-chart", className),
|
|
2620
|
+
"aria-label": ariaLabel,
|
|
2621
|
+
children: data.length === 0 ? /* @__PURE__ */ jsx19("span", {
|
|
2622
|
+
children: "No data"
|
|
2623
|
+
}) : /* @__PURE__ */ jsxs14(Fragment11, {
|
|
2624
|
+
children: [
|
|
2625
|
+
/* @__PURE__ */ jsxs14(ComposedChart, {
|
|
2626
|
+
data,
|
|
2627
|
+
ariaLabel: `${ariaLabel} plot`,
|
|
2628
|
+
children: [
|
|
2629
|
+
/* @__PURE__ */ jsx19(ComposedChart.CartesianGrid, {}),
|
|
2630
|
+
/* @__PURE__ */ jsx19(ComposedChart.XAxis, {
|
|
2631
|
+
dataKey: "offset",
|
|
2632
|
+
type: "number",
|
|
2633
|
+
domain: [0, Math.max(1, ...offsets.keys())],
|
|
2634
|
+
tickFormatter: (value) => offsets.get(Number(value)) ?? (data[0]?.label.replace(/\d+(?:\.\d+)?/, String(value)) || String(value))
|
|
2635
|
+
}),
|
|
2636
|
+
/* @__PURE__ */ jsx19(ComposedChart.YAxis, {
|
|
2637
|
+
domain: [0, 100],
|
|
2638
|
+
width: 42,
|
|
2639
|
+
tickFormatter: (value) => `${value}%`
|
|
2640
|
+
}),
|
|
2641
|
+
series.map((item, index) => /* @__PURE__ */ jsxs14(Fragment10, {
|
|
2642
|
+
children: [
|
|
2643
|
+
/* @__PURE__ */ jsx19(ComposedChart.Line, {
|
|
2644
|
+
dataKey: `rate${index}`,
|
|
2645
|
+
name: item.label,
|
|
2646
|
+
stroke: `var(--atbl-chart-${index % 8 + 1})`,
|
|
2647
|
+
connectNulls: false
|
|
2648
|
+
}),
|
|
2649
|
+
/* @__PURE__ */ jsx19(ComposedChart.Line, {
|
|
2650
|
+
dataKey: `tail${index}`,
|
|
2651
|
+
name: item.label,
|
|
2652
|
+
stroke: `var(--atbl-chart-${index % 8 + 1})`,
|
|
2653
|
+
strokeDasharray: "3 3",
|
|
2654
|
+
dot: false,
|
|
2655
|
+
activeDot: false,
|
|
2656
|
+
connectNulls: false
|
|
2657
|
+
})
|
|
2658
|
+
]
|
|
2659
|
+
}, item.id)),
|
|
2660
|
+
/* @__PURE__ */ jsx19(ComposedChart.Tooltip, {
|
|
2661
|
+
filterNull: false,
|
|
2662
|
+
content: ({ active, payload }) => {
|
|
2663
|
+
const offset = payload?.[0]?.payload.offset;
|
|
2664
|
+
if (!active || typeof offset !== "number")
|
|
2665
|
+
return null;
|
|
2666
|
+
return /* @__PURE__ */ jsxs14(TooltipSurface, {
|
|
2667
|
+
"data-variant": "chart",
|
|
2668
|
+
role: "tooltip",
|
|
2669
|
+
children: [
|
|
2670
|
+
/* @__PURE__ */ jsx19("strong", {
|
|
2671
|
+
children: offsets.get(offset)
|
|
2672
|
+
}),
|
|
2673
|
+
series.map((item) => {
|
|
2674
|
+
const point = item.points.find((point) => point.offset === offset);
|
|
2675
|
+
if (!point)
|
|
2676
|
+
return null;
|
|
2677
|
+
return /* @__PURE__ */ jsxs14("span", {
|
|
2678
|
+
children: [
|
|
2679
|
+
item.label,
|
|
2680
|
+
":",
|
|
2681
|
+
" ",
|
|
2682
|
+
point.rate === null ? "Not observed" : `${formatPercent2(point.rate)} · ${formatValue(point.retainedCount)} / ${formatValue(item.cohortSize)} ${unit}${point.incomplete ? " · Incomplete period" : ""}`
|
|
2683
|
+
]
|
|
2684
|
+
}, item.id);
|
|
2685
|
+
})
|
|
2686
|
+
]
|
|
2687
|
+
});
|
|
2688
|
+
}
|
|
2689
|
+
})
|
|
2690
|
+
]
|
|
2691
|
+
}),
|
|
2692
|
+
/* @__PURE__ */ jsx19(ChartLegend, {
|
|
2693
|
+
"aria-label": `${ariaLabel} cohorts`,
|
|
2694
|
+
children: series.map((item, index) => /* @__PURE__ */ jsxs14(ChartLegend.Item, {
|
|
2695
|
+
children: [
|
|
2696
|
+
/* @__PURE__ */ jsx19(ChartLegend.Marker, {
|
|
2697
|
+
kind: "square",
|
|
2698
|
+
color: `var(--atbl-chart-${index % 8 + 1})`
|
|
1553
2699
|
}),
|
|
1554
|
-
/* @__PURE__ */
|
|
1555
|
-
className: "altertable-selectable-bars-label",
|
|
2700
|
+
/* @__PURE__ */ jsx19(ChartLegend.Label, {
|
|
1556
2701
|
children: item.label
|
|
1557
2702
|
})
|
|
1558
2703
|
]
|
|
1559
|
-
})
|
|
1560
|
-
},
|
|
1561
|
-
|
|
1562
|
-
|
|
2704
|
+
}, item.id))
|
|
2705
|
+
}),
|
|
2706
|
+
/* @__PURE__ */ jsx19("ul", {
|
|
2707
|
+
className: "altertable-sr-only",
|
|
2708
|
+
children: series.map((item) => /* @__PURE__ */ jsxs14("li", {
|
|
2709
|
+
children: [
|
|
2710
|
+
item.label,
|
|
2711
|
+
item.points.map((point) => /* @__PURE__ */ jsxs14("span", {
|
|
2712
|
+
children: [
|
|
2713
|
+
" ",
|
|
2714
|
+
"· ",
|
|
2715
|
+
point.label,
|
|
2716
|
+
":",
|
|
2717
|
+
" ",
|
|
2718
|
+
point.rate === null ? "Not observed" : `${formatPercent2(point.rate)} · ${formatValue(point.retainedCount)} / ${formatValue(item.cohortSize)} ${unit}${point.incomplete ? " · Incomplete period" : ""}`
|
|
2719
|
+
]
|
|
2720
|
+
}, point.offset))
|
|
2721
|
+
]
|
|
2722
|
+
}, item.id))
|
|
2723
|
+
})
|
|
2724
|
+
]
|
|
2725
|
+
})
|
|
2726
|
+
});
|
|
2727
|
+
}
|
|
2728
|
+
// src/react/ui/JourneyChart.tsx
|
|
2729
|
+
import { useLayoutEffect as useLayoutEffect3, useRef as useRef4, useState as useState7 } from "react";
|
|
2730
|
+
|
|
2731
|
+
// src/react/ui/journey-flow.ts
|
|
2732
|
+
var journeyOutcomeLabels = {
|
|
2733
|
+
truncated: "More steps",
|
|
2734
|
+
"drop-off": "Drop-off",
|
|
2735
|
+
converted: "Converted",
|
|
2736
|
+
end: "End of path"
|
|
2737
|
+
};
|
|
2738
|
+
function journeyNodes(path) {
|
|
2739
|
+
const nodes = path.steps.map((step, depth) => ({
|
|
2740
|
+
key: JSON.stringify([depth, "event", step.event, step.property]),
|
|
2741
|
+
label: step.property === null ? step.event : `${step.event} · ${step.property}`,
|
|
2742
|
+
depth,
|
|
2743
|
+
outcome: "event"
|
|
2744
|
+
}));
|
|
2745
|
+
const outcome = path.truncated ? "truncated" : path.converted === false ? "drop-off" : path.converted === true ? "converted" : "end";
|
|
2746
|
+
nodes.push({
|
|
2747
|
+
key: JSON.stringify([nodes.length, outcome]),
|
|
2748
|
+
label: journeyOutcomeLabels[outcome],
|
|
2749
|
+
depth: nodes.length,
|
|
2750
|
+
outcome
|
|
2751
|
+
});
|
|
2752
|
+
return nodes;
|
|
2753
|
+
}
|
|
2754
|
+
function buildJourneyFlow(paths, expandedBranch) {
|
|
2755
|
+
const nodes = new Map;
|
|
2756
|
+
const links = new Map;
|
|
2757
|
+
for (const path of paths) {
|
|
2758
|
+
const fullSequence = journeyNodes(path);
|
|
2759
|
+
if (path.truncated)
|
|
2760
|
+
fullSequence.pop();
|
|
2761
|
+
const sequence = expandedBranch === undefined ? fullSequence : fullSequence.flatMap((node, index) => {
|
|
2762
|
+
const branchKeys = fullSequence.slice(0, index + 1).map((step) => step.key);
|
|
2763
|
+
const parentExpanded = index <= 1 || branchKeys.slice(0, index).every((key, depth) => expandedBranch[depth] === key);
|
|
2764
|
+
if (!parentExpanded)
|
|
2765
|
+
return [];
|
|
2766
|
+
return [
|
|
2767
|
+
{
|
|
2768
|
+
...node,
|
|
2769
|
+
key: JSON.stringify(branchKeys),
|
|
2770
|
+
branchKeys,
|
|
2771
|
+
expandable: index >= 1 && index < fullSequence.length - 1,
|
|
2772
|
+
expanded: branchKeys.every((key, depth) => expandedBranch[depth] === key)
|
|
2773
|
+
}
|
|
2774
|
+
];
|
|
2775
|
+
});
|
|
2776
|
+
sequence.forEach((node, index) => {
|
|
2777
|
+
const current = nodes.get(node.key) ?? {
|
|
2778
|
+
...node,
|
|
2779
|
+
count: 0,
|
|
2780
|
+
pathKeys: []
|
|
2781
|
+
};
|
|
2782
|
+
current.count += path.count;
|
|
2783
|
+
if ("expandable" in node && node.expandable)
|
|
2784
|
+
current.expandable = true;
|
|
2785
|
+
current.pathKeys.push(path.id);
|
|
2786
|
+
nodes.set(node.key, current);
|
|
2787
|
+
const previous = sequence[index - 1];
|
|
2788
|
+
if (!previous)
|
|
2789
|
+
return;
|
|
2790
|
+
const key = JSON.stringify([previous.key, node.key]);
|
|
2791
|
+
const link = links.get(key) ?? {
|
|
2792
|
+
key,
|
|
2793
|
+
source: previous.key,
|
|
2794
|
+
target: node.key,
|
|
2795
|
+
count: 0,
|
|
2796
|
+
pathKeys: []
|
|
2797
|
+
};
|
|
2798
|
+
link.count += path.count;
|
|
2799
|
+
link.pathKeys.push(path.id);
|
|
2800
|
+
links.set(key, link);
|
|
2801
|
+
});
|
|
2802
|
+
}
|
|
2803
|
+
return { nodes: [...nodes.values()], links: [...links.values()] };
|
|
2804
|
+
}
|
|
2805
|
+
function limitJourneyFlow(graph, visibleEventsByDepth = {}) {
|
|
2806
|
+
const nodes = [];
|
|
2807
|
+
const replacements = new Map;
|
|
2808
|
+
for (const depth of new Set(graph.nodes.map((node) => node.depth))) {
|
|
2809
|
+
const column = graph.nodes.filter((node) => node.depth === depth).sort((a, b) => Number(Boolean(b.expanded)) - Number(Boolean(a.expanded)) || b.count - a.count || a.key.localeCompare(b.key));
|
|
2810
|
+
const events = column.filter((node) => node.outcome === "event");
|
|
2811
|
+
const outcomes = column.filter((node) => node.outcome !== "event");
|
|
2812
|
+
const limit = visibleEventsByDepth[depth] ?? 3;
|
|
2813
|
+
if (events.length <= limit) {
|
|
2814
|
+
nodes.push(...column);
|
|
2815
|
+
continue;
|
|
2816
|
+
}
|
|
2817
|
+
nodes.push(...events.slice(0, limit), ...outcomes);
|
|
2818
|
+
const hidden = events.slice(limit);
|
|
2819
|
+
const key = JSON.stringify(["other", depth]);
|
|
2820
|
+
hidden.forEach((node) => replacements.set(node.key, key));
|
|
2821
|
+
nodes.push({
|
|
2822
|
+
key,
|
|
2823
|
+
label: "More events",
|
|
2824
|
+
depth,
|
|
2825
|
+
outcome: "other",
|
|
2826
|
+
hiddenBranchCount: hidden.length,
|
|
2827
|
+
count: hidden.reduce((sum, node) => sum + node.count, 0),
|
|
2828
|
+
pathKeys: [...new Set(hidden.flatMap((node) => node.pathKeys))]
|
|
2829
|
+
});
|
|
2830
|
+
}
|
|
2831
|
+
const links = new Map;
|
|
2832
|
+
for (const link of graph.links) {
|
|
2833
|
+
const source = replacements.get(link.source) ?? link.source;
|
|
2834
|
+
const target = replacements.get(link.target) ?? link.target;
|
|
2835
|
+
const key = JSON.stringify([source, target]);
|
|
2836
|
+
const existing = links.get(key);
|
|
2837
|
+
if (existing) {
|
|
2838
|
+
existing.count += link.count;
|
|
2839
|
+
existing.pathKeys.push(...link.pathKeys);
|
|
2840
|
+
} else {
|
|
2841
|
+
links.set(key, {
|
|
2842
|
+
...link,
|
|
2843
|
+
key,
|
|
2844
|
+
source,
|
|
2845
|
+
target,
|
|
2846
|
+
pathKeys: [...link.pathKeys]
|
|
2847
|
+
});
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
return { nodes, links: [...links.values()] };
|
|
2851
|
+
}
|
|
2852
|
+
|
|
2853
|
+
// src/react/ui/JourneyChart.tsx
|
|
2854
|
+
import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2855
|
+
function JourneyChart({
|
|
2856
|
+
paths,
|
|
2857
|
+
unit,
|
|
2858
|
+
ariaLabel,
|
|
2859
|
+
className,
|
|
2860
|
+
formatValue = formatNumber2
|
|
2861
|
+
}) {
|
|
2862
|
+
const ids = new Set;
|
|
2863
|
+
let total = 0;
|
|
2864
|
+
for (const path of paths) {
|
|
2865
|
+
invariant(path.id.trim().length > 0 && !ids.has(path.id), "journey path chart IDs must be nonblank and unique.");
|
|
2866
|
+
ids.add(path.id);
|
|
2867
|
+
invariant(Number.isFinite(path.count) && path.count >= 0, "journey chart counts must be finite and nonnegative.");
|
|
2868
|
+
total += path.count;
|
|
2869
|
+
invariant(path.steps.length > 0 && path.steps.every((step) => step.event.trim().length > 0), "journey chart paths require nonblank events.");
|
|
2870
|
+
}
|
|
2871
|
+
invariant(Number.isFinite(total), "journey chart total count must be finite.");
|
|
2872
|
+
return /* @__PURE__ */ jsx20("section", {
|
|
2873
|
+
className: classNames("altertable-journey-chart", className),
|
|
2874
|
+
"aria-label": ariaLabel,
|
|
2875
|
+
children: paths.length === 0 ? /* @__PURE__ */ jsx20("span", {
|
|
2876
|
+
children: "No data"
|
|
2877
|
+
}) : /* @__PURE__ */ jsx20(JourneyFlow, {
|
|
2878
|
+
paths,
|
|
2879
|
+
unit,
|
|
2880
|
+
formatValue
|
|
2881
|
+
}, JSON.stringify(paths))
|
|
2882
|
+
});
|
|
2883
|
+
}
|
|
2884
|
+
function JourneyFlow({
|
|
2885
|
+
paths,
|
|
2886
|
+
unit,
|
|
2887
|
+
formatValue
|
|
2888
|
+
}) {
|
|
2889
|
+
const [expandedBranch, setExpandedBranch] = useState7([]);
|
|
2890
|
+
const [visibleEventsByDepth, setVisibleEventsByDepth] = useState7({});
|
|
2891
|
+
const graph = limitJourneyFlow(buildJourneyFlow(paths, expandedBranch), visibleEventsByDepth);
|
|
2892
|
+
const scrollRef = useRef4(null);
|
|
2893
|
+
const pendingDepth = useRef4(null);
|
|
2894
|
+
const depthCount = Math.max(0, ...graph.nodes.map((node) => node.depth)) + 1;
|
|
2895
|
+
const startingCount = graph.nodes.filter((node) => node.depth === 0).reduce((sum, node) => sum + node.count, 0);
|
|
2896
|
+
function percent(count) {
|
|
2897
|
+
return formatPercent2(startingCount > 0 ? count / startingCount : null);
|
|
2898
|
+
}
|
|
2899
|
+
const nodeWidth = 176;
|
|
2900
|
+
const nodeHeight = 96;
|
|
2901
|
+
const barWidth = 16;
|
|
2902
|
+
const rowSpacing = nodeHeight + 80;
|
|
2903
|
+
const columnSpacing = nodeWidth + 112;
|
|
2904
|
+
const width = 34 + nodeWidth + (depthCount - 1) * columnSpacing;
|
|
2905
|
+
function columnX(depth) {
|
|
2906
|
+
return 10 + depth * columnSpacing;
|
|
2907
|
+
}
|
|
2908
|
+
useLayoutEffect3(() => {
|
|
2909
|
+
const depth = pendingDepth.current;
|
|
2910
|
+
const container = scrollRef.current;
|
|
2911
|
+
if (depth === null || !container || depth >= depthCount)
|
|
2912
|
+
return;
|
|
2913
|
+
pendingDepth.current = null;
|
|
2914
|
+
const left = columnX(depth);
|
|
2915
|
+
const right = left + nodeWidth + 24;
|
|
2916
|
+
const nextLeft = left < container.scrollLeft ? left - 24 : right > container.scrollLeft + container.clientWidth ? right - container.clientWidth : container.scrollLeft;
|
|
2917
|
+
if (nextLeft !== container.scrollLeft)
|
|
2918
|
+
container.scrollTo({
|
|
2919
|
+
left: Math.max(0, nextLeft),
|
|
2920
|
+
behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "instant" : "smooth"
|
|
2921
|
+
});
|
|
2922
|
+
});
|
|
2923
|
+
const columns = Array.from({ length: depthCount }, (_, depth) => graph.nodes.filter((node) => node.depth === depth).sort((a, b) => {
|
|
2924
|
+
function rank(node) {
|
|
2925
|
+
return node.outcome === "event" ? 0 : node.outcome === "other" ? 1 : 2;
|
|
2926
|
+
}
|
|
2927
|
+
return rank(a) - rank(b) || b.count - a.count || a.key.localeCompare(b.key);
|
|
2928
|
+
}));
|
|
2929
|
+
const positions = new Map;
|
|
2930
|
+
columns.forEach((column, depth) => {
|
|
2931
|
+
const parent = columns[depth - 1]?.find((node) => node.expanded);
|
|
2932
|
+
const parentY = parent ? positions.get(parent.key)?.y : undefined;
|
|
2933
|
+
const top = Math.max(104, (parentY ?? 104) - (column.length > 1 ? rowSpacing : 0));
|
|
2934
|
+
column.forEach((node, index) => positions.set(node.key, {
|
|
2935
|
+
node,
|
|
2936
|
+
x: columnX(depth),
|
|
2937
|
+
y: top + index * rowSpacing
|
|
2938
|
+
}));
|
|
2939
|
+
});
|
|
2940
|
+
const height = Math.max(280, ...[...positions.values()].map((position) => position.y + nodeHeight + 40));
|
|
2941
|
+
const outgoingOffsets = new Map;
|
|
2942
|
+
const incomingOffsets = new Map;
|
|
2943
|
+
const orderedLinks = [...graph.links].sort((a, b) => (positions.get(a.source)?.y ?? 0) - (positions.get(b.source)?.y ?? 0) || (positions.get(a.target)?.y ?? 0) - (positions.get(b.target)?.y ?? 0));
|
|
2944
|
+
const links = orderedLinks.flatMap((link) => {
|
|
2945
|
+
const source = positions.get(link.source);
|
|
2946
|
+
const target = positions.get(link.target);
|
|
2947
|
+
if (!source || !target || link.count <= 0 || startingCount <= 0)
|
|
2948
|
+
return [];
|
|
2949
|
+
const x0 = source.x + barWidth;
|
|
2950
|
+
const x1 = target.x;
|
|
2951
|
+
const mid = (x0 + x1) / 2;
|
|
2952
|
+
const sourceTop = source.y + (outgoingOffsets.get(link.source) ?? 0);
|
|
2953
|
+
const targetTop = target.y + (incomingOffsets.get(link.target) ?? 0);
|
|
2954
|
+
const thickness = link.count / startingCount * nodeHeight;
|
|
2955
|
+
outgoingOffsets.set(link.source, (outgoingOffsets.get(link.source) ?? 0) + thickness);
|
|
2956
|
+
incomingOffsets.set(link.target, (incomingOffsets.get(link.target) ?? 0) + thickness);
|
|
2957
|
+
return [
|
|
2958
|
+
{
|
|
2959
|
+
muted: target.node.outcome === "end" || target.node.outcome === "other",
|
|
2960
|
+
d: `M${x0},${sourceTop} C${mid},${sourceTop} ${mid},${targetTop} ${x1},${targetTop} L${x1},${targetTop + thickness} C${mid},${targetTop + thickness} ${mid},${sourceTop + thickness} ${x0},${sourceTop + thickness} Z`
|
|
2961
|
+
}
|
|
2962
|
+
];
|
|
2963
|
+
});
|
|
2964
|
+
return /* @__PURE__ */ jsx20("div", {
|
|
2965
|
+
ref: scrollRef,
|
|
2966
|
+
className: "altertable-journey-scroll",
|
|
2967
|
+
children: /* @__PURE__ */ jsxs15("div", {
|
|
2968
|
+
className: "altertable-journey-plot",
|
|
2969
|
+
style: { minWidth: width, minHeight: height },
|
|
2970
|
+
children: [
|
|
2971
|
+
/* @__PURE__ */ jsxs15("svg", {
|
|
2972
|
+
width: "100%",
|
|
2973
|
+
height: "100%",
|
|
2974
|
+
"aria-hidden": "true",
|
|
2975
|
+
focusable: "false",
|
|
2976
|
+
children: [
|
|
2977
|
+
/* @__PURE__ */ jsx20("path", {
|
|
2978
|
+
d: links.filter((link) => !link.muted).map((link) => link.d).join(" "),
|
|
2979
|
+
fill: "color-mix(in srgb, var(--atbl-chart-5) 22%, var(--atbl-surface))"
|
|
2980
|
+
}),
|
|
2981
|
+
/* @__PURE__ */ jsx20("path", {
|
|
2982
|
+
d: links.filter((link) => link.muted).map((link) => link.d).join(" "),
|
|
2983
|
+
fill: "color-mix(in srgb, var(--atbl-muted) 22%, var(--atbl-surface))"
|
|
2984
|
+
})
|
|
2985
|
+
]
|
|
2986
|
+
}),
|
|
2987
|
+
columns.map((column, depth) => /* @__PURE__ */ jsx20("span", {
|
|
2988
|
+
className: "altertable-journey-stage",
|
|
2989
|
+
style: { left: columnX(depth) },
|
|
2990
|
+
children: column.length > 0 && column.every((node) => node.outcome !== "event" && node.outcome !== "other") ? "Outcome" : `Step ${depth + 1}`
|
|
2991
|
+
}, depth)),
|
|
2992
|
+
[...positions.values()].map(({ node, x, y }) => /* @__PURE__ */ jsxs15("div", {
|
|
2993
|
+
className: "altertable-journey-node",
|
|
2994
|
+
style: {
|
|
2995
|
+
left: x,
|
|
2996
|
+
top: y,
|
|
2997
|
+
width: nodeWidth,
|
|
2998
|
+
height: startingCount > 0 ? node.count / startingCount * nodeHeight : 0
|
|
2999
|
+
},
|
|
3000
|
+
children: [
|
|
3001
|
+
/* @__PURE__ */ jsx20("div", {
|
|
3002
|
+
className: "altertable-journey-bar",
|
|
3003
|
+
"data-event": node.outcome === "event" || undefined,
|
|
3004
|
+
"aria-hidden": "true",
|
|
3005
|
+
style: { width: barWidth }
|
|
3006
|
+
}),
|
|
3007
|
+
/* @__PURE__ */ jsxs15("section", {
|
|
3008
|
+
"aria-label": `${node.label}, step ${node.depth + 1}, ${formatValue(node.count)} ${unit}`,
|
|
3009
|
+
className: "altertable-journey-label",
|
|
3010
|
+
children: [
|
|
3011
|
+
/* @__PURE__ */ jsx20("span", {
|
|
3012
|
+
className: "altertable-journey-name",
|
|
3013
|
+
title: node.label,
|
|
3014
|
+
children: node.label
|
|
3015
|
+
}),
|
|
3016
|
+
/* @__PURE__ */ jsxs15("span", {
|
|
3017
|
+
children: [
|
|
3018
|
+
formatValue(node.count),
|
|
3019
|
+
" ",
|
|
3020
|
+
unit,
|
|
3021
|
+
" · ",
|
|
3022
|
+
percent(node.count)
|
|
3023
|
+
]
|
|
3024
|
+
}),
|
|
3025
|
+
node.hiddenBranchCount ? /* @__PURE__ */ jsxs15("span", {
|
|
3026
|
+
children: [
|
|
3027
|
+
node.hiddenBranchCount,
|
|
3028
|
+
" hidden",
|
|
3029
|
+
" ",
|
|
3030
|
+
node.hiddenBranchCount === 1 ? "event" : "events"
|
|
3031
|
+
]
|
|
3032
|
+
}) : null
|
|
3033
|
+
]
|
|
3034
|
+
}),
|
|
3035
|
+
node.expandable && /* @__PURE__ */ jsx20(Button2, {
|
|
3036
|
+
variant: "outline",
|
|
3037
|
+
size: "icon-compact",
|
|
3038
|
+
className: "altertable-journey-expand",
|
|
3039
|
+
"aria-label": `${node.expanded ? "Less" : "More"} after ${node.label}, step ${node.depth + 1}`,
|
|
3040
|
+
"aria-expanded": Boolean(node.expanded),
|
|
3041
|
+
onClick: () => {
|
|
3042
|
+
pendingDepth.current = node.expanded ? null : node.depth + 1;
|
|
3043
|
+
setVisibleEventsByDepth((limits) => Object.fromEntries(Object.entries(limits).filter(([depth]) => Number(depth) <= node.depth)));
|
|
3044
|
+
setExpandedBranch(node.expanded ? (node.branchKeys ?? []).slice(0, -1) : node.branchKeys ?? []);
|
|
3045
|
+
},
|
|
3046
|
+
children: node.expanded ? "−" : "›"
|
|
3047
|
+
}),
|
|
3048
|
+
node.outcome === "other" && /* @__PURE__ */ jsx20(Button2, {
|
|
3049
|
+
size: "compact",
|
|
3050
|
+
variant: "ghost",
|
|
3051
|
+
className: "altertable-journey-more",
|
|
3052
|
+
"aria-label": `Show more events at step ${node.depth + 1}`,
|
|
3053
|
+
onClick: () => setVisibleEventsByDepth((limits) => ({
|
|
3054
|
+
...limits,
|
|
3055
|
+
[node.depth]: (limits[node.depth] ?? 3) + 3
|
|
3056
|
+
})),
|
|
3057
|
+
children: "Show more events"
|
|
3058
|
+
})
|
|
3059
|
+
]
|
|
3060
|
+
}, node.key))
|
|
3061
|
+
]
|
|
3062
|
+
})
|
|
1563
3063
|
});
|
|
1564
3064
|
}
|
|
1565
3065
|
export {
|
|
@@ -1567,8 +3067,11 @@ export {
|
|
|
1567
3067
|
AppIcon2 as AppIcon,
|
|
1568
3068
|
AreaChart,
|
|
1569
3069
|
BarChart,
|
|
3070
|
+
ChartLegend,
|
|
1570
3071
|
Checkbox2 as Checkbox,
|
|
1571
|
-
|
|
3072
|
+
CheckboxGroup,
|
|
3073
|
+
ChoicePicker2 as ChoicePicker,
|
|
3074
|
+
ComposedChart,
|
|
1572
3075
|
DataApp,
|
|
1573
3076
|
DataAppProvider2 as DataAppProvider,
|
|
1574
3077
|
DataAppSkeleton,
|
|
@@ -1580,19 +3083,38 @@ export {
|
|
|
1580
3083
|
DataWidget2 as DataWidget,
|
|
1581
3084
|
DateRangePicker2 as DateRangePicker,
|
|
1582
3085
|
DimensionPicker2 as DimensionPicker,
|
|
3086
|
+
FilterActions2 as FilterActions,
|
|
3087
|
+
FilterBar2 as FilterBar,
|
|
3088
|
+
FunnelChart,
|
|
1583
3089
|
GettingStarted,
|
|
1584
3090
|
GlossaryDefinition,
|
|
1585
3091
|
GlossaryExplanation,
|
|
1586
3092
|
GradientScroll2 as GradientScroll,
|
|
1587
3093
|
HelpPopover2 as HelpPopover,
|
|
3094
|
+
JourneyChart,
|
|
1588
3095
|
LineChart,
|
|
1589
3096
|
LiveControl2 as LiveControl,
|
|
3097
|
+
Menu2 as Menu,
|
|
3098
|
+
PressButton as MenuButton,
|
|
3099
|
+
MenuItem2 as MenuItem,
|
|
3100
|
+
MenuPopover2 as MenuPopover,
|
|
3101
|
+
MenuSection2 as MenuSection,
|
|
3102
|
+
MenuSeparator2 as MenuSeparator,
|
|
3103
|
+
MenuTrigger2 as MenuTrigger,
|
|
1590
3104
|
MetricWidget2 as MetricWidget,
|
|
3105
|
+
NumberField2 as NumberField,
|
|
3106
|
+
NumberFilterPicker2 as NumberFilterPicker,
|
|
3107
|
+
NumberRangeField2 as NumberRangeField,
|
|
1591
3108
|
PieChart,
|
|
1592
3109
|
PresentStory2 as PresentStory,
|
|
3110
|
+
Radio,
|
|
3111
|
+
RadioGroup,
|
|
3112
|
+
RetentionChart,
|
|
1593
3113
|
ScatterChart,
|
|
1594
3114
|
SearchField2 as SearchField,
|
|
1595
3115
|
SearchMatch2 as SearchMatch,
|
|
3116
|
+
SegmentedControl,
|
|
3117
|
+
Select2 as Select,
|
|
1596
3118
|
Sheet2 as Sheet,
|
|
1597
3119
|
Tab2 as Tab,
|
|
1598
3120
|
TabList2 as TabList,
|
|
@@ -1603,12 +3125,15 @@ export {
|
|
|
1603
3125
|
TextWidget2 as TextWidget,
|
|
1604
3126
|
Tooltip2 as Tooltip,
|
|
1605
3127
|
TooltipProvider2 as TooltipProvider,
|
|
3128
|
+
VariableBar2 as VariableBar,
|
|
1606
3129
|
VisualizationWidget2 as VisualizationWidget,
|
|
1607
3130
|
injectDataAppShellStyles,
|
|
1608
3131
|
injectDataAppStyles2 as injectDataAppStyles,
|
|
3132
|
+
isPlainKeyEvent2 as isPlainKeyEvent,
|
|
1609
3133
|
searchItems2 as searchItems,
|
|
1610
3134
|
useAppVariables2 as useAppVariables,
|
|
3135
|
+
useSvgId,
|
|
1611
3136
|
useViewTab2 as useViewTab
|
|
1612
3137
|
};
|
|
1613
3138
|
|
|
1614
|
-
//# debugId=
|
|
3139
|
+
//# debugId=6CFD84691873BB8C64756E2164756E21
|