@manta-eu/react 0.6.0 → 0.8.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.
Files changed (100) hide show
  1. package/dist/components/button/index.d.ts +1 -0
  2. package/dist/components/button/index.d.ts.map +1 -1
  3. package/dist/components/button/index.js +17 -1
  4. package/dist/components/button/index.js.map +1 -1
  5. package/dist/components/chart/ForecastChart.d.ts +41 -0
  6. package/dist/components/chart/ForecastChart.d.ts.map +1 -0
  7. package/dist/components/chart/ForecastChart.js +110 -0
  8. package/dist/components/chart/ForecastChart.js.map +1 -0
  9. package/dist/components/chart/ForecastEventCard.d.ts +28 -0
  10. package/dist/components/chart/ForecastEventCard.d.ts.map +1 -0
  11. package/dist/components/chart/ForecastEventCard.js +56 -0
  12. package/dist/components/chart/ForecastEventCard.js.map +1 -0
  13. package/dist/components/chart/ForecastHoverCard.d.ts +23 -0
  14. package/dist/components/chart/ForecastHoverCard.d.ts.map +1 -0
  15. package/dist/components/chart/ForecastHoverCard.js +22 -0
  16. package/dist/components/chart/ForecastHoverCard.js.map +1 -0
  17. package/dist/components/chart/ForecastLegend.d.ts +33 -0
  18. package/dist/components/chart/ForecastLegend.d.ts.map +1 -0
  19. package/dist/components/chart/ForecastLegend.js +38 -0
  20. package/dist/components/chart/ForecastLegend.js.map +1 -0
  21. package/dist/components/chart/forecast-model.d.ts +70 -0
  22. package/dist/components/chart/forecast-model.d.ts.map +1 -0
  23. package/dist/components/chart/forecast-model.js +89 -0
  24. package/dist/components/chart/forecast-model.js.map +1 -0
  25. package/dist/components/chart/forecast.d.ts +62 -0
  26. package/dist/components/chart/forecast.d.ts.map +1 -0
  27. package/dist/components/chart/forecast.js +90 -0
  28. package/dist/components/chart/forecast.js.map +1 -0
  29. package/dist/components/chart/index.d.ts +3 -0
  30. package/dist/components/chart/index.d.ts.map +1 -1
  31. package/dist/components/chart/index.js +1 -0
  32. package/dist/components/chart/index.js.map +1 -1
  33. package/dist/components/modal/index.d.ts +4 -0
  34. package/dist/components/modal/index.d.ts.map +1 -1
  35. package/dist/components/modal/index.js +11 -3
  36. package/dist/components/modal/index.js.map +1 -1
  37. package/dist/components/popover/index.d.ts +2 -1
  38. package/dist/components/popover/index.d.ts.map +1 -1
  39. package/dist/components/popover/index.js +2 -1
  40. package/dist/components/popover/index.js.map +1 -1
  41. package/dist/components/sheet/index.d.ts +13 -19
  42. package/dist/components/sheet/index.d.ts.map +1 -1
  43. package/dist/components/sheet/index.js +8 -2
  44. package/dist/components/sheet/index.js.map +1 -1
  45. package/dist/components/table/index.d.ts +7 -1
  46. package/dist/components/table/index.d.ts.map +1 -1
  47. package/dist/components/table/index.js +18 -12
  48. package/dist/components/table/index.js.map +1 -1
  49. package/dist/globals.css +1582 -2770
  50. package/dist/hooks/use-connection-problems.d.ts.map +1 -1
  51. package/dist/hooks/useAuth.d.ts +1 -0
  52. package/dist/hooks/useAuth.d.ts.map +1 -1
  53. package/dist/hooks/useAuth.js +1 -0
  54. package/dist/hooks/useAuth.js.map +1 -1
  55. package/dist/hooks/useValidateActionPlan.d.ts.map +1 -1
  56. package/dist/providers/AuthContext.d.ts +1 -0
  57. package/dist/providers/AuthContext.d.ts.map +1 -1
  58. package/dist/providers/AuthContext.js.map +1 -1
  59. package/dist/routes/integrations/components/connect-forms/MetaAdsConnectForm.d.ts +1 -1
  60. package/dist/routes/integrations/components/connect-forms/MetaAdsConnectForm.d.ts.map +1 -1
  61. package/dist/routes/integrations/components/connect-forms/MetaAdsConnectForm.js +64 -11
  62. package/dist/routes/integrations/components/connect-forms/MetaAdsConnectForm.js.map +1 -1
  63. package/dist/routes/integrations/components/connect-forms/OAuthSetupConnectForm.d.ts.map +1 -1
  64. package/dist/routes/integrations/components/connect-forms/OAuthSetupConnectForm.js +8 -2
  65. package/dist/routes/integrations/components/connect-forms/OAuthSetupConnectForm.js.map +1 -1
  66. package/dist/routes/integrations/components/connect-forms/meta-page-tree.d.ts +5 -0
  67. package/dist/routes/integrations/components/connect-forms/meta-page-tree.d.ts.map +1 -0
  68. package/dist/routes/integrations/components/connect-forms/meta-page-tree.js +16 -0
  69. package/dist/routes/integrations/components/connect-forms/meta-page-tree.js.map +1 -0
  70. package/dist/routes/integrations/components/connect-forms/oauth-setup-phase.d.ts +2 -1
  71. package/dist/routes/integrations/components/connect-forms/oauth-setup-phase.d.ts.map +1 -1
  72. package/dist/routes/integrations/components/connect-forms/oauth-setup-phase.js +4 -1
  73. package/dist/routes/integrations/components/connect-forms/oauth-setup-phase.js.map +1 -1
  74. package/dist/routes/integrations/connection-view.d.ts.map +1 -1
  75. package/dist/routes/integrations/connection-view.js +1 -0
  76. package/dist/routes/integrations/connection-view.js.map +1 -1
  77. package/package.json +16 -17
  78. package/src/components/button/index.tsx +17 -1
  79. package/src/components/chart/ForecastChart.tsx +383 -0
  80. package/src/components/chart/ForecastEventCard.tsx +163 -0
  81. package/src/components/chart/ForecastHoverCard.tsx +73 -0
  82. package/src/components/chart/ForecastLegend.tsx +97 -0
  83. package/src/components/chart/forecast-model.ts +130 -0
  84. package/src/components/chart/forecast.ts +144 -0
  85. package/src/components/chart/index.tsx +3 -0
  86. package/src/components/modal/index.tsx +17 -2
  87. package/src/components/popover/index.tsx +2 -1
  88. package/src/components/sheet/index.tsx +29 -6
  89. package/src/components/table/index.tsx +42 -12
  90. package/src/globals.css +2 -0
  91. package/src/hooks/useAuth.ts +2 -0
  92. package/src/providers/AuthContext.ts +1 -0
  93. package/src/routes/integrations/components/connect-forms/MetaAdsConnectForm.tsx +131 -15
  94. package/src/routes/integrations/components/connect-forms/OAuthSetupConnectForm.tsx +8 -2
  95. package/src/routes/integrations/components/connect-forms/meta-page-tree.ts +17 -0
  96. package/src/routes/integrations/components/connect-forms/oauth-setup-phase.ts +6 -0
  97. package/src/routes/integrations/connection-view.ts +1 -0
  98. package/src/themes/manta-dark.css +2 -0
  99. package/src/themes/manta.css +2 -0
  100. package/src/themes/standard.css +2 -0
@@ -1 +1 @@
1
- {"version":3,"file":"connection-view.js","sourceRoot":"","sources":["../../../src/routes/integrations/connection-view.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connection-view.js","sourceRoot":"","sources":["../../../src/routes/integrations/connection-view.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@manta-eu/react",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "The Manta design system, knowledge-graph UI and agent components",
5
5
  "homepage": "https://developers.manta.eu",
6
6
  "keywords": [
@@ -1139,12 +1139,12 @@
1139
1139
  "usehooks-ts": "^3.1.1",
1140
1140
  "zod": "^4.5.0",
1141
1141
  "zustand": "^5.0.2",
1142
- "@manta-eu/auth": "0.6.0",
1143
- "@manta-eu/datalog-parser": "0.6.0",
1144
- "@manta-eu/client": "0.6.0",
1145
- "@manta-eu/ldo": "0.6.0",
1146
- "@manta-eu/lib": "0.6.0",
1147
- "@manta-eu/rdf-lib": "0.6.0"
1142
+ "@manta-eu/auth": "0.8.0",
1143
+ "@manta-eu/client": "0.8.0",
1144
+ "@manta-eu/datalog-parser": "0.8.0",
1145
+ "@manta-eu/ldo": "0.8.0",
1146
+ "@manta-eu/rdf-lib": "0.8.0",
1147
+ "@manta-eu/lib": "0.8.0"
1148
1148
  },
1149
1149
  "peerDependencies": {
1150
1150
  "@ulb-darmstadt/shacl-form": "^2.1.1-rc1",
@@ -1155,11 +1155,11 @@
1155
1155
  "react-dom": "^19.0.0"
1156
1156
  },
1157
1157
  "devDependencies": {
1158
- "@storybook/addon-docs": "^10.2.1",
1159
- "@storybook/addon-links": "^10.2.1",
1160
- "@storybook/react": "^10.2.1",
1161
- "@storybook/react-vite": "^10.2.1",
1162
- "@tailwindcss/postcss": "^4.1.17",
1158
+ "@storybook/addon-docs": "^10.6.4",
1159
+ "@storybook/addon-links": "^10.6.4",
1160
+ "@storybook/react": "^10.6.4",
1161
+ "@storybook/react-vite": "^10.6.4",
1162
+ "@tailwindcss/postcss": "^4.3.3",
1163
1163
  "@types/js-cookie": "^3.0.6",
1164
1164
  "@types/mustache": "^4.2.5",
1165
1165
  "@types/ramda": "^0.31.1",
@@ -1174,12 +1174,12 @@
1174
1174
  "react": "^19.2.3",
1175
1175
  "react-dom": "^19.2.3",
1176
1176
  "rimraf": "^6.1.3",
1177
- "storybook": "^10.2.1",
1178
- "tailwindcss": "^4.1.17",
1177
+ "storybook": "^10.6.4",
1178
+ "tailwindcss": "^4.3.3",
1179
1179
  "tsc-alias": "^1.9.5",
1180
1180
  "typescript": "^5.9.3",
1181
1181
  "uuid": "^13.0.0",
1182
- "vite": "^7.3.1",
1182
+ "vite": "^8.3.5",
1183
1183
  "vitest": "^4.1.8",
1184
1184
  "@manta-eu/eslint-config": "0.1.0"
1185
1185
  },
@@ -1200,9 +1200,8 @@
1200
1200
  "access": "public"
1201
1201
  },
1202
1202
  "scripts": {
1203
- "build": "pnpm run build:lib",
1203
+ "build": "rimraf dist && postcss src/globals.css -o dist/globals.css && tsc --project tsconfig.build.json && tsc-alias --project tsconfig.build.json --resolve-full-paths",
1204
1204
  "build-storybook": "NODE_OPTIONS=\"$NODE_OPTIONS --max-old-space-size=6144\" storybook build",
1205
- "build:lib": "rimraf dist && postcss src/globals.css -o dist/globals.css && tsc --project tsconfig.build.json && tsc-alias --project tsconfig.build.json --resolve-full-paths",
1206
1205
  "lint": "eslint .",
1207
1206
  "storybook": "storybook dev -p 6007",
1208
1207
  "test": "vitest --run",
@@ -1,5 +1,6 @@
1
1
  import { Slot } from "@radix-ui/react-slot";
2
2
  import { Loader2 } from "lucide-react";
3
+ import { Children } from "react";
3
4
  import type { ComponentProps, ComponentPropsWithoutRef, ComponentType, ElementType, ReactNode, Ref } from "react";
4
5
  import { tv } from "tailwind-variants";
5
6
  import { cn, type ClassProp, type VariantPropsOf } from "#style";
@@ -8,6 +9,21 @@ function Loader(props: ComponentProps<typeof Loader2>) {
8
9
  return <Loader2 {...props} className={cn("size-4 animate-spin text-inherit", props.className)} />;
9
10
  }
10
11
 
12
+ function isText(child: unknown): child is string | number {
13
+ return typeof child === "string" || typeof child === "number";
14
+ }
15
+
16
+ // Text needs an element around it, or the loading state's `[&>*]:invisible` leaves it under the spinner.
17
+ export function wrapTextChildren(children: ReactNode): ReactNode[] {
18
+ const parts = Children.toArray(children);
19
+ return parts.flatMap<ReactNode>((child, index) => {
20
+ if (!isText(child)) return [child];
21
+ if (index > 0 && isText(parts[index - 1])) return [];
22
+ const end = parts.findIndex((part, at) => at > index && !isText(part));
23
+ return [<span key={`text-${index}`}>{parts.slice(index, end === -1 ? undefined : end).join("")}</span>];
24
+ });
25
+ }
26
+
11
27
  export const buttonConfig = {
12
28
  base: [
13
29
  "inline-flex items-center justify-center text-center gap-1.5 transition-[color_150ms_ease,transform_100ms_ease-out] motion-reduce:transition-none",
@@ -165,7 +181,7 @@ export function Button(props: ButtonProps) {
165
181
  {LeftIconComponent && <LeftIconComponent />}
166
182
  {label && <span>{label}</span>}
167
183
  {RightIconComponent && <RightIconComponent />}
168
- {children}
184
+ {wrapTextChildren(children)}
169
185
  {isLoading && (
170
186
  <span className="absolute inset-0 flex items-center justify-center" data-loader-wrapper aria-hidden="true">
171
187
  <Loader data-loader />
@@ -0,0 +1,383 @@
1
+ import { isPresent } from "@manta-eu/lib/present";
2
+ import { useEffect, useId, useMemo, useRef, useState } from "react";
3
+ import { Area, CartesianGrid, ComposedChart, Line, ReferenceDot, ReferenceLine, XAxis, YAxis } from "recharts";
4
+ import { renderAnnotations } from "./annotations";
5
+ import { ChartTooltip } from "./chart";
6
+ import { ChartFrame } from "./ChartFrame";
7
+ import {
8
+ forecastMargin,
9
+ forecastXAxisHeight,
10
+ formatCompact,
11
+ formatWhole,
12
+ nearestMetric,
13
+ partKey,
14
+ plotGeometry,
15
+ rangeKey,
16
+ valueAtY,
17
+ type ForecastEvent,
18
+ type ForecastPart,
19
+ type ForecastPlanShare,
20
+ } from "./forecast";
21
+ import {
22
+ betweenName,
23
+ buildForecastModel,
24
+ forecastParts as parts,
25
+ gainName,
26
+ metricColor,
27
+ withoutColor,
28
+ type ForecastBetween,
29
+ type ForecastMetric,
30
+ } from "./forecast-model";
31
+ import { ForecastEventCard, ForecastEventDot } from "./ForecastEventCard";
32
+ import { ForecastHoverCard } from "./ForecastHoverCard";
33
+ import { ForecastLegend } from "./ForecastLegend";
34
+ import { Popover, PopoverAnchor } from "#components/popover/index";
35
+ import { cn } from "#style";
36
+
37
+ export type ForecastChartProps = {
38
+ /** One row per x value; each row carries `x` and a number per metric key. */
39
+ data: Record<string, unknown>[];
40
+ /** The x value of Today: lines are solid up to it and dotted after. Omit it for a history-only chart. */
41
+ today?: string | number;
42
+ metrics: ForecastMetric[];
43
+ /** Legend name of the shading between a metric and its `withoutPlans` line. */
44
+ gainLabel?: string;
45
+ /** A faint fill between two metrics, such as profit between revenue and costs. */
46
+ between?: ForecastBetween[];
47
+ goal?: { value: number; label: string };
48
+ events?: ForecastEvent[];
49
+ /** Called when an event's dot or its card link is chosen; without it, an event's `href` is a plain link. */
50
+ onEventSelect?: (event: ForecastEvent) => void;
51
+ /** The plans behind each projected point of a metric, shown in its hover card (the first two). */
52
+ planShares?: ForecastPlanShare[];
53
+ /** Formats axis ticks. */
54
+ formatValue?: (value: number) => string;
55
+ /** Formats the figures in the hover card. */
56
+ formatDetail?: (value: number) => string;
57
+ /** Follows the signed gap in the hover card: "+$2,664 with the plans". */
58
+ changeLabel?: string;
59
+ projectedLabel?: string;
60
+ /** The tag on the Today line. */
61
+ todayLabel?: string;
62
+ /** Legend names of the solid and dotted parts of a single metric. */
63
+ legendLabels?: { historic?: string; projected?: string };
64
+ title?: string;
65
+ height?: number;
66
+ className?: string;
67
+ };
68
+
69
+ const projectedDash = "0.5 6";
70
+ const noBetween: ForecastBetween[] = [];
71
+ const noEvents: ForecastEvent[] = [];
72
+ const noPlanShares: ForecastPlanShare[] = [];
73
+
74
+ function TodayTag(props: { text: string; viewBox?: { x?: number; y?: number } }) {
75
+ const { text, viewBox } = props;
76
+ if (viewBox?.x === undefined || viewBox.y === undefined) {
77
+ return null;
78
+ }
79
+ return (
80
+ <foreignObject
81
+ x={viewBox.x - 100}
82
+ y={viewBox.y}
83
+ width={200}
84
+ height={20}
85
+ className="pointer-events-none overflow-visible"
86
+ >
87
+ <div className="flex justify-center">
88
+ <span className="h-5 rounded-full border border-default-border bg-card px-2 typography-tiny-medium leading-5 text-default-foreground">
89
+ {text}
90
+ </span>
91
+ </div>
92
+ </foreignObject>
93
+ );
94
+ }
95
+
96
+ export function ForecastChart(props: ForecastChartProps) {
97
+ const {
98
+ data,
99
+ today,
100
+ metrics,
101
+ gainLabel = "Added by plans",
102
+ between = noBetween,
103
+ goal,
104
+ events = noEvents,
105
+ onEventSelect,
106
+ planShares = noPlanShares,
107
+ formatValue = formatCompact,
108
+ formatDetail = formatWhole,
109
+ changeLabel = "with the plans",
110
+ projectedLabel = "projected",
111
+ todayLabel = "Today",
112
+ legendLabels,
113
+ title,
114
+ height = 320,
115
+ className,
116
+ } = props;
117
+ const id = useId().replace(/:/g, "");
118
+ const fadeId = `forecast-fade-${id}`;
119
+ const cardId = `forecast-event-${id}`;
120
+ const plotRef = useRef<HTMLDivElement>(null);
121
+ const closeTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
122
+ const [activeEvent, setActiveEvent] = useState<{ index: number; cx: number; cy: number }>();
123
+ const [focusKey, setFocusKey] = useState<string>();
124
+
125
+ const model = useMemo(
126
+ () =>
127
+ buildForecastModel({
128
+ data,
129
+ ...(today === undefined ? {} : { today }),
130
+ metrics,
131
+ between,
132
+ gainLabel,
133
+ ...(goal ? { goal } : {}),
134
+ ...(legendLabels ? { legendLabels } : {}),
135
+ }),
136
+ [data, today, metrics, between, goal, gainLabel, legendLabels],
137
+ );
138
+
139
+ const emptyKeys = model.emptyKeys.join(", ");
140
+ useEffect(() => {
141
+ if (isPresent(emptyKeys)) {
142
+ console.warn(`ForecastChart: no numbers in data for ${emptyKeys}; check the metric keys`);
143
+ }
144
+ }, [emptyKeys]);
145
+
146
+ const missingToday = model.todayMissing ? String(today) : "";
147
+ useEffect(() => {
148
+ if (isPresent(missingToday)) {
149
+ console.warn(
150
+ `ForecastChart: today "${missingToday}" matches no row's x; the Today line and projection are hidden`,
151
+ );
152
+ }
153
+ }, [missingToday]);
154
+
155
+ const { overlay, withPlans, rows, domain, ticks, firstColor } = model;
156
+ const gainOpacity: Record<ForecastPart, number> = overlay ? { past: 0.5, ahead: 0.34 } : { past: 0.24, ahead: 0.14 };
157
+ const betweenOpacity: Record<ForecastPart, number> = { past: 0.08, ahead: 0.05 };
158
+ const geometry = plotGeometry(height);
159
+ const focusedMetric = model.hoverMetrics.find((metric) => metric.key === focusKey) ?? model.hoverMetrics[0];
160
+
161
+ function followPointer(state: { chartY?: number; activePayload?: { payload?: Record<string, unknown> }[] }) {
162
+ const row = state.activePayload?.[0]?.payload;
163
+ if (state.chartY === undefined || row === undefined) {
164
+ return;
165
+ }
166
+ const nearest = nearestMetric(
167
+ row,
168
+ metrics.map((metric) => metric.key),
169
+ valueAtY(state.chartY, geometry.top, geometry.height, domain),
170
+ );
171
+ if (nearest !== focusKey) {
172
+ setFocusKey(nearest);
173
+ }
174
+ }
175
+
176
+ function cancelClose() {
177
+ clearTimeout(closeTimer.current);
178
+ }
179
+
180
+ function closeSoon() {
181
+ cancelClose();
182
+ closeTimer.current = setTimeout(() => setActiveEvent(undefined), 150);
183
+ }
184
+
185
+ function closeUnlessInside(next: EventTarget | null) {
186
+ const inside = [plotRef.current, document.getElementById(cardId)].some(
187
+ (container) => next instanceof Node && container?.contains(next),
188
+ );
189
+ if (!inside) {
190
+ setActiveEvent(undefined);
191
+ }
192
+ }
193
+
194
+ function showEvent(index: number, cx: number, cy: number) {
195
+ cancelClose();
196
+ setActiveEvent({ index, cx, cy });
197
+ }
198
+
199
+ function rangeArea(name: string, part: ForecastPart, color: string, opacity: number) {
200
+ return (
201
+ <Area
202
+ key={rangeKey(name, part)}
203
+ dataKey={rangeKey(name, part)}
204
+ type="monotone"
205
+ fill={color}
206
+ fillOpacity={opacity}
207
+ stroke="none"
208
+ tooltipType="none"
209
+ activeDot={false}
210
+ isAnimationActive={false}
211
+ />
212
+ );
213
+ }
214
+
215
+ function line(key: string, part: ForecastPart, color: string, width: number, withPlans: boolean) {
216
+ const activeDot = { r: 3.5, fill: color, stroke: "var(--color-card)", strokeWidth: 1.5 };
217
+ return (
218
+ <Line
219
+ key={partKey(key, part)}
220
+ dataKey={partKey(key, part)}
221
+ type="monotone"
222
+ stroke={color}
223
+ strokeWidth={width}
224
+ strokeLinejoin="round"
225
+ strokeLinecap="round"
226
+ strokeDasharray={part === "ahead" && withPlans ? projectedDash : undefined}
227
+ dot={false}
228
+ activeDot={activeEvent ? false : activeDot}
229
+ tooltipType="none"
230
+ isAnimationActive={false}
231
+ />
232
+ );
233
+ }
234
+
235
+ const shownEvent = activeEvent ? events[activeEvent.index] : undefined;
236
+
237
+ return (
238
+ <div className={cn("w-full", className)}>
239
+ {isPresent(title) ? (
240
+ <p className="typography-small-medium mb-2 text-center text-default-foreground">{title}</p>
241
+ ) : null}
242
+ <ForecastLegend items={model.legendItems} />
243
+ <div ref={plotRef} className="relative">
244
+ <ChartFrame config={model.config} height={height}>
245
+ <ComposedChart accessibilityLayer data={rows} margin={forecastMargin} onMouseMove={followPointer}>
246
+ <defs>
247
+ <linearGradient id={fadeId} x1="0" y1="0" x2="0" y2="1">
248
+ <stop offset="0%" stopColor={model.fadeColor} stopOpacity={overlay ? 0.3 : 0.34} />
249
+ <stop offset="100%" stopColor={model.fadeColor} stopOpacity={0} />
250
+ </linearGradient>
251
+ </defs>
252
+ <CartesianGrid vertical={false} strokeDasharray="2 3" />
253
+ <XAxis
254
+ dataKey="x"
255
+ height={forecastXAxisHeight}
256
+ tickLine={false}
257
+ axisLine={false}
258
+ tickMargin={12}
259
+ minTickGap={24}
260
+ />
261
+ <YAxis
262
+ tickLine={false}
263
+ axisLine={false}
264
+ width={56}
265
+ domain={domain}
266
+ ticks={ticks}
267
+ interval={0}
268
+ tickFormatter={formatValue}
269
+ allowDataOverflow
270
+ />
271
+ {model.fadeKeys.map(({ part, key }) => (
272
+ <Area
273
+ key={`fade-${part}`}
274
+ dataKey={partKey(key, part)}
275
+ baseValue="dataMin"
276
+ type="monotone"
277
+ stroke="none"
278
+ fill={`url(#${fadeId})`}
279
+ tooltipType="none"
280
+ activeDot={false}
281
+ isAnimationActive={false}
282
+ />
283
+ ))}
284
+ {between.flatMap((_, index) =>
285
+ parts.map((part) => rangeArea(betweenName(index), part, firstColor, betweenOpacity[part])),
286
+ )}
287
+ {withPlans.flatMap((entry) =>
288
+ parts.map((part) =>
289
+ rangeArea(gainName(entry.index), part, metricColor(entry.metric, entry.index), gainOpacity[part]),
290
+ ),
291
+ )}
292
+ {isPresent(goal)
293
+ ? renderAnnotations([
294
+ { kind: "reference-line", axis: "y", value: goal.value, label: goal.label, tone: "neutral" },
295
+ ])
296
+ : null}
297
+ {model.todayIndex >= 0 && today !== undefined ? (
298
+ <ReferenceLine
299
+ x={today}
300
+ stroke="var(--color-default-foreground)"
301
+ strokeOpacity={0.4}
302
+ label={<TodayTag text={todayLabel} />}
303
+ />
304
+ ) : null}
305
+ <ChartTooltip
306
+ active={shownEvent ? false : undefined}
307
+ cursor={{ stroke: "var(--color-default-border)" }}
308
+ content={
309
+ <ForecastHoverCard
310
+ metric={focusedMetric}
311
+ formatDetail={formatDetail}
312
+ changeLabel={changeLabel}
313
+ projectedLabel={projectedLabel}
314
+ planShares={planShares}
315
+ />
316
+ }
317
+ />
318
+ {withPlans.flatMap((entry) =>
319
+ parts.map((part) => line(entry.without.key, part, withoutColor, overlay ? 1.5 : 2, false)),
320
+ )}
321
+ {metrics.flatMap((metric, index) =>
322
+ (["ahead", "past"] as const).map((part) =>
323
+ line(metric.key, part, metricColor(metric, index), index === 0 ? 2.5 : 2.25, true),
324
+ ),
325
+ )}
326
+ {events.map((event, index) => {
327
+ const metricIndex = metrics.findIndex((metric) => metric.key === event.metric);
328
+ const metric = metrics[metricIndex];
329
+ const value = data.find((row) => row.x === event.x)?.[event.metric];
330
+ if (typeof value !== "number" || metric === undefined) {
331
+ return null;
332
+ }
333
+ return (
334
+ <ReferenceDot
335
+ key={`${event.metric}-${event.x}-${event.title}`}
336
+ x={event.x}
337
+ y={value}
338
+ shape={(dot: { cx?: number; cy?: number }) => (
339
+ <ForecastEventDot
340
+ event={event}
341
+ color={metricColor(metric, metricIndex)}
342
+ {...(dot.cx === undefined ? {} : { cx: dot.cx })}
343
+ {...(dot.cy === undefined ? {} : { cy: dot.cy })}
344
+ active={activeEvent?.index === index}
345
+ cardId={cardId}
346
+ {...(onEventSelect ? { onSelect: onEventSelect } : {})}
347
+ onShow={(cx, cy) => showEvent(index, cx, cy)}
348
+ onLeave={closeSoon}
349
+ onBlur={closeUnlessInside}
350
+ />
351
+ )}
352
+ />
353
+ );
354
+ })}
355
+ </ComposedChart>
356
+ </ChartFrame>
357
+ <Popover
358
+ key={activeEvent?.index}
359
+ open={shownEvent !== undefined}
360
+ onOpenChange={(open) => !open && setActiveEvent(undefined)}
361
+ >
362
+ <PopoverAnchor asChild>
363
+ <span
364
+ aria-hidden="true"
365
+ className="pointer-events-none absolute size-0"
366
+ style={{ left: activeEvent?.cx ?? 0, top: activeEvent?.cy ?? 0 }}
367
+ />
368
+ </PopoverAnchor>
369
+ {shownEvent ? (
370
+ <ForecastEventCard
371
+ event={shownEvent}
372
+ id={cardId}
373
+ {...(onEventSelect ? { onSelect: onEventSelect } : {})}
374
+ onEnter={cancelClose}
375
+ onLeave={closeSoon}
376
+ onBlur={closeUnlessInside}
377
+ />
378
+ ) : null}
379
+ </Popover>
380
+ </div>
381
+ </div>
382
+ );
383
+ }
@@ -0,0 +1,163 @@
1
+ import { isPresent } from "@manta-eu/lib/present";
2
+ import { ArrowUpRight } from "lucide-react";
3
+ import type { FocusEvent, KeyboardEvent, ReactNode } from "react";
4
+ import type { ForecastEvent } from "./forecast";
5
+ import { PopoverArrow, PopoverContent } from "#components/popover/index";
6
+ import { cn } from "#style";
7
+
8
+ const eventLabels: Record<ForecastEvent["status"], string> = {
9
+ applied: "Applied",
10
+ waiting: "Waiting for your approval",
11
+ };
12
+
13
+ export function eventLabel(event: ForecastEvent): string {
14
+ return event.label ?? eventLabels[event.status];
15
+ }
16
+
17
+ type ForecastEventDotProps = {
18
+ event: ForecastEvent;
19
+ color: string;
20
+ cx?: number;
21
+ cy?: number;
22
+ active: boolean;
23
+ cardId: string;
24
+ onSelect?: (event: ForecastEvent) => void;
25
+ onShow: (cx: number, cy: number) => void;
26
+ onLeave: () => void;
27
+ onBlur: (next: EventTarget | null) => void;
28
+ };
29
+
30
+ /** A dot on a metric's line. With `onSelect` it acts on click or Enter; otherwise an `href` makes it a plain link. */
31
+ export function ForecastEventDot(props: ForecastEventDotProps) {
32
+ const { event, color, cx, cy, active, cardId, onSelect, onShow, onLeave, onBlur } = props;
33
+ if (cx === undefined || cy === undefined) {
34
+ return null;
35
+ }
36
+ const label = `${eventLabel(event)}: ${event.title}`;
37
+ const waiting = event.status === "waiting";
38
+ const circles = (
39
+ <>
40
+ <circle cx={cx} cy={cy} r={14} fill="transparent" />
41
+ <circle
42
+ cx={cx}
43
+ cy={cy}
44
+ r={10}
45
+ fill={color}
46
+ className={cn(
47
+ "opacity-0 transition-opacity group-hover/event:opacity-20 group-focus-visible/event:opacity-35",
48
+ active && "opacity-20",
49
+ )}
50
+ />
51
+ <circle
52
+ cx={cx}
53
+ cy={cy}
54
+ r={active ? 6.5 : 5}
55
+ fill={waiting ? "var(--color-card)" : color}
56
+ stroke={waiting ? color : "var(--color-card)"}
57
+ strokeWidth={2}
58
+ />
59
+ </>
60
+ );
61
+ const shared = {
62
+ "aria-label": label,
63
+ "aria-describedby": active ? cardId : undefined,
64
+ className: "group/event cursor-pointer outline-none",
65
+ onMouseEnter: () => onShow(cx, cy),
66
+ onMouseLeave: onLeave,
67
+ onFocus: () => onShow(cx, cy),
68
+ onBlur: (focusEvent: FocusEvent) => onBlur(focusEvent.relatedTarget),
69
+ };
70
+
71
+ if (onSelect === undefined && isPresent(event.href)) {
72
+ return (
73
+ <a href={event.href} {...shared}>
74
+ {circles}
75
+ </a>
76
+ );
77
+ }
78
+
79
+ if (onSelect === undefined) {
80
+ return (
81
+ <g role="img" tabIndex={0} {...shared} className="group/event outline-none">
82
+ {circles}
83
+ </g>
84
+ );
85
+ }
86
+
87
+ function handleKeyDown(keyEvent: KeyboardEvent<SVGGElement>) {
88
+ if (keyEvent.key === "Enter" || keyEvent.key === " ") {
89
+ keyEvent.preventDefault();
90
+ onSelect?.(event);
91
+ }
92
+ }
93
+
94
+ return (
95
+ <g role="button" tabIndex={0} {...shared} onClick={() => onSelect(event)} onKeyDown={handleKeyDown}>
96
+ {circles}
97
+ </g>
98
+ );
99
+ }
100
+
101
+ type ForecastEventCardProps = {
102
+ event: ForecastEvent;
103
+ id: string;
104
+ onSelect?: (event: ForecastEvent) => void;
105
+ onEnter: () => void;
106
+ onLeave: () => void;
107
+ onBlur: (next: EventTarget | null) => void;
108
+ };
109
+
110
+ /** The dark card over a dot: its label, title, value and, with an `href`, an "Open plan" link. */
111
+ export function ForecastEventCard(props: ForecastEventCardProps) {
112
+ const { event, id, onSelect, onEnter, onLeave, onBlur } = props;
113
+ const link: ReactNode = isPresent(event.href) ? (
114
+ <a
115
+ href={event.href}
116
+ onClick={(clickEvent) => {
117
+ if (onSelect) {
118
+ clickEvent.preventDefault();
119
+ onSelect(event);
120
+ }
121
+ }}
122
+ className="mt-1 inline-flex items-center gap-1 self-start typography-small underline underline-offset-2"
123
+ >
124
+ {event.linkLabel ?? "Open plan"}
125
+ <ArrowUpRight className="size-3.5" aria-hidden="true" />
126
+ </a>
127
+ ) : null;
128
+ return (
129
+ <PopoverContent
130
+ id={id}
131
+ role={isPresent(event.href) ? undefined : "tooltip"}
132
+ side="top"
133
+ sideOffset={12}
134
+ collisionPadding={8}
135
+ onOpenAutoFocus={(focusEvent) => focusEvent.preventDefault()}
136
+ onCloseAutoFocus={(focusEvent) => focusEvent.preventDefault()}
137
+ className={cn(
138
+ "flex w-max min-w-50 max-w-65 flex-col gap-1 rounded-md-v4 border-0 px-3.5 pt-2.5 pb-3",
139
+ "bg-inverse text-inverse-foreground shadow-xl ring-1 ring-inverse-foreground/10",
140
+ !isPresent(event.href) && "pointer-events-none",
141
+ )}
142
+ onMouseEnter={onEnter}
143
+ onMouseLeave={onLeave}
144
+ onBlur={(focusEvent) => onBlur(focusEvent.relatedTarget)}
145
+ >
146
+ <span className="typography-tiny opacity-70">{eventLabel(event)}</span>
147
+ <strong className="typography-body-medium">{event.title}</strong>
148
+ {isPresent(event.value) ? (
149
+ <span
150
+ className="typography-small-semibold tabular-nums"
151
+ style={{ color: "color-mix(in srgb, var(--color-accent-foreground) 55%, var(--color-inverse-foreground))" }}
152
+ >
153
+ {event.value}
154
+ {isPresent(event.valueNote) ? (
155
+ <small className="ml-1 typography-tiny opacity-70">{event.valueNote}</small>
156
+ ) : null}
157
+ </span>
158
+ ) : null}
159
+ {link}
160
+ <PopoverArrow className="fill-inverse" width={12} height={6} />
161
+ </PopoverContent>
162
+ );
163
+ }