@titan-design/react-ui 0.2.7 → 0.3.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/dist/bodymap-BhG55xHM.d.mts +318 -0
- package/dist/bodymap-BhG55xHM.d.ts +318 -0
- package/dist/bodymap.d.mts +3 -0
- package/dist/bodymap.d.ts +3 -0
- package/dist/bodymap.js +2670 -0
- package/dist/bodymap.js.map +1 -0
- package/dist/bodymap.mjs +1101 -0
- package/dist/bodymap.mjs.map +1 -0
- package/dist/{chunk-5SSKGS6E.mjs → chunk-7XPB4NAO.mjs} +124 -4
- package/dist/chunk-7XPB4NAO.mjs.map +1 -0
- package/dist/{chunk-3VLOBS6O.mjs → chunk-P7TSQUN6.mjs} +3 -3
- package/dist/{chunk-3VLOBS6O.mjs.map → chunk-P7TSQUN6.mjs.map} +1 -1
- package/dist/chunk-TOD2WQBG.mjs +1048 -0
- package/dist/chunk-TOD2WQBG.mjs.map +1 -0
- package/dist/index.d.mts +375 -249
- package/dist/index.d.ts +375 -249
- package/dist/index.js +1259 -918
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1314 -2107
- package/dist/index.mjs.map +1 -1
- package/dist/theme/index.d.mts +260 -0
- package/dist/theme/index.d.ts +260 -0
- package/dist/theme/index.js +122 -2
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +2 -2
- package/dist/theme/tokens-css.d.mts +5 -3
- package/dist/theme/tokens-css.d.ts +5 -3
- package/dist/theme/tokens-css.js +318 -9
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +1 -1
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/package.json +10 -1
- package/src/bodymap.ts +37 -0
- package/src/components/custom/Table/Table.test.tsx +27 -0
- package/src/components/custom/Table/Table.tsx +33 -17
- package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +118 -0
- package/src/components/custom/Workout/ActiveWorkoutPage.test.tsx +171 -0
- package/src/components/custom/Workout/ActiveWorkoutPage.tsx +447 -0
- package/src/components/custom/Workout/BodyMap.tsx +5 -6
- package/src/components/custom/Workout/BodyMapDetailPanel.tsx +7 -8
- package/src/components/custom/Workout/CapacityBandChart.tsx +1 -1
- package/src/components/custom/Workout/ExerciseCard.test.tsx +24 -0
- package/src/components/custom/Workout/ExerciseCard.tsx +28 -15
- package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +225 -0
- package/src/components/custom/Workout/ExerciseDetailPage.test.tsx +128 -0
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +630 -0
- package/src/components/custom/Workout/InputBar.tsx +6 -7
- package/src/components/custom/Workout/MesoCard.tsx +4 -2
- package/src/components/custom/Workout/MesoProgressBar.test.tsx +12 -0
- package/src/components/custom/Workout/MesoProgressBar.tsx +5 -1
- package/src/components/custom/Workout/MesoStatusCard.tsx +5 -5
- package/src/components/custom/Workout/PlaceholderStrip.tsx +2 -3
- package/src/components/custom/Workout/PrHistoryModal.tsx +5 -5
- package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +257 -0
- package/src/components/custom/Workout/ProgramPlanningPage.test.tsx +142 -0
- package/src/components/custom/Workout/ProgramPlanningPage.tsx +422 -0
- package/src/components/custom/Workout/ReadinessCheck.tsx +7 -8
- package/src/components/custom/Workout/RestTimer.test.tsx +10 -0
- package/src/components/custom/Workout/RestTimer.tsx +11 -9
- package/src/components/custom/Workout/SetRow.tsx +13 -19
- package/src/components/custom/Workout/StatusDot.tsx +1 -0
- package/src/components/custom/Workout/StrengthTrendChart.tsx +5 -5
- package/src/components/custom/Workout/TempoDisplay.tsx +3 -1
- package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +212 -0
- package/src/components/custom/Workout/TrainingStatusPage.test.tsx +131 -0
- package/src/components/custom/Workout/TrainingStatusPage.tsx +321 -0
- package/src/components/custom/Workout/VelocityStrip.test.tsx +10 -0
- package/src/components/custom/Workout/VelocityStrip.tsx +16 -15
- package/src/components/custom/Workout/WeekRow.tsx +1 -1
- package/src/components/custom/Workout/WorkoutCard.tsx +5 -3
- package/src/components/custom/Workout/index.ts +71 -32
- package/src/theme/config.completeness.test.ts +77 -0
- package/src/theme/config.ts +151 -0
- package/src/theme/manifest/design-freeze.test.ts +155 -0
- package/src/theme/manifest/extract-css-properties.fixture.html +29 -0
- package/src/theme/manifest/extract-css-properties.test.ts +112 -0
- package/src/theme/tokens-css.ts +5 -3
- package/src/theme/workout-tokens.ts +12 -14
- package/src/utils/index.ts +10 -0
- package/src/utils/workout-format.test.ts +81 -0
- package/src/utils/workout-format.ts +83 -0
- package/dist/chunk-5SSKGS6E.mjs.map +0 -1
|
@@ -0,0 +1,1048 @@
|
|
|
1
|
+
import { getValidatedElevation, getBaseSurfaceColor, getElevationSurface, getElevationShadow } from './chunk-P7TSQUN6.mjs';
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import { twMerge } from 'tailwind-merge';
|
|
4
|
+
import { useState, useMemo, useSyncExternalStore, Fragment as Fragment$1 } from 'react';
|
|
5
|
+
import { View, Pressable, Text } from 'react-native';
|
|
6
|
+
import { Fragment, jsx as jsx$1, jsxs as jsxs$1 } from 'react/jsx-runtime';
|
|
7
|
+
export { Fragment } from 'react/jsx-runtime';
|
|
8
|
+
|
|
9
|
+
function cn(...inputs) {
|
|
10
|
+
return twMerge(clsx(inputs));
|
|
11
|
+
}
|
|
12
|
+
function getTheme() {
|
|
13
|
+
if (typeof document === "undefined") return "dark";
|
|
14
|
+
return document.documentElement.classList.contains("light") ? "light" : "dark";
|
|
15
|
+
}
|
|
16
|
+
var listeners = [];
|
|
17
|
+
function subscribe(listener) {
|
|
18
|
+
listeners = [...listeners, listener];
|
|
19
|
+
if (typeof document !== "undefined" && listeners.length === 1) {
|
|
20
|
+
const observer = new MutationObserver((mutations) => {
|
|
21
|
+
for (const mutation of mutations) {
|
|
22
|
+
if (mutation.type === "attributes" && mutation.attributeName === "class") {
|
|
23
|
+
listeners.forEach((l) => l());
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
observer.observe(document.documentElement, {
|
|
28
|
+
attributes: true,
|
|
29
|
+
attributeFilter: ["class"]
|
|
30
|
+
});
|
|
31
|
+
subscribe._observer = observer;
|
|
32
|
+
}
|
|
33
|
+
return () => {
|
|
34
|
+
listeners = listeners.filter((l) => l !== listener);
|
|
35
|
+
if (listeners.length === 0 && subscribe._observer) {
|
|
36
|
+
subscribe._observer.disconnect();
|
|
37
|
+
subscribe._observer = null;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function getSnapshot() {
|
|
42
|
+
return getTheme();
|
|
43
|
+
}
|
|
44
|
+
function getServerSnapshot() {
|
|
45
|
+
return "dark";
|
|
46
|
+
}
|
|
47
|
+
function useTheme() {
|
|
48
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
49
|
+
}
|
|
50
|
+
function wrapJsx(fn) {
|
|
51
|
+
return function(type, props, ...rest) {
|
|
52
|
+
if (props && typeof props.className === "string" && props.className) {
|
|
53
|
+
const cn2 = props.className;
|
|
54
|
+
const cssStyle = { $$css: true, [cn2]: cn2 };
|
|
55
|
+
const existing = props.style;
|
|
56
|
+
props = {
|
|
57
|
+
...props,
|
|
58
|
+
style: existing ? [cssStyle, existing] : cssStyle
|
|
59
|
+
};
|
|
60
|
+
delete props.className;
|
|
61
|
+
}
|
|
62
|
+
return fn(type, props, ...rest);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
var jsx = wrapJsx(jsx$1);
|
|
66
|
+
var jsxs = wrapJsx(jsxs$1);
|
|
67
|
+
|
|
68
|
+
// src/components/ui/card/Card.tsx
|
|
69
|
+
var variantStyles = {
|
|
70
|
+
elevated: "",
|
|
71
|
+
// Will be set dynamically via elevation system
|
|
72
|
+
outline: "border-2 border-border-strong",
|
|
73
|
+
// Thicker border with stronger contrast
|
|
74
|
+
filled: "",
|
|
75
|
+
// Will be set dynamically via elevation system
|
|
76
|
+
accent: "border border-border",
|
|
77
|
+
subtle: "border border-border-subtle"
|
|
78
|
+
};
|
|
79
|
+
function Card({
|
|
80
|
+
variant = "elevated",
|
|
81
|
+
elevation = 2,
|
|
82
|
+
isInteractive = false,
|
|
83
|
+
isLoading = false,
|
|
84
|
+
onPress,
|
|
85
|
+
borderColor,
|
|
86
|
+
bgColor,
|
|
87
|
+
accentColor,
|
|
88
|
+
accentWidth,
|
|
89
|
+
className,
|
|
90
|
+
children,
|
|
91
|
+
style,
|
|
92
|
+
skeletonHasHeader = true,
|
|
93
|
+
skeletonHasFooter = false,
|
|
94
|
+
skeletonContentLines = 3,
|
|
95
|
+
...props
|
|
96
|
+
}) {
|
|
97
|
+
const isClickable = isInteractive || !!onPress;
|
|
98
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
99
|
+
const theme = useTheme();
|
|
100
|
+
const elevationLevel = useMemo(() => {
|
|
101
|
+
const validated = getValidatedElevation("card", elevation);
|
|
102
|
+
if (variant === "outline" || variant === "accent" || variant === "subtle") {
|
|
103
|
+
return 1;
|
|
104
|
+
}
|
|
105
|
+
return validated;
|
|
106
|
+
}, [variant, elevation]);
|
|
107
|
+
const baseColor = useMemo(() => getBaseSurfaceColor(theme), [theme]);
|
|
108
|
+
const surfaceColor = useMemo(() => {
|
|
109
|
+
if ((variant === "outline" || variant === "accent") && theme === "light") {
|
|
110
|
+
return "#FAFAFA";
|
|
111
|
+
}
|
|
112
|
+
if (variant === "subtle") {
|
|
113
|
+
return getElevationSurface(baseColor, 0, theme);
|
|
114
|
+
}
|
|
115
|
+
return getElevationSurface(baseColor, elevationLevel, theme);
|
|
116
|
+
}, [baseColor, elevationLevel, theme, variant]);
|
|
117
|
+
const shadowStyle = useMemo(() => {
|
|
118
|
+
const shouldHover = isHovered && isClickable;
|
|
119
|
+
return getElevationShadow(baseColor, elevationLevel, theme, shouldHover);
|
|
120
|
+
}, [baseColor, elevationLevel, theme, isHovered, isClickable]);
|
|
121
|
+
const cardContent = isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
122
|
+
skeletonHasHeader && /* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
123
|
+
/* @__PURE__ */ jsx(View, { className: "h-5 w-1/3 bg-interactive-disabled rounded" }),
|
|
124
|
+
/* @__PURE__ */ jsx(View, { className: "mt-2 h-4 w-2/3 bg-interactive-disabled rounded" })
|
|
125
|
+
] }),
|
|
126
|
+
/* @__PURE__ */ jsx(CardContent, { children: Array.from({ length: skeletonContentLines }).map((_, i) => /* @__PURE__ */ jsx(
|
|
127
|
+
View,
|
|
128
|
+
{
|
|
129
|
+
className: cn(
|
|
130
|
+
"h-4 bg-interactive-disabled rounded",
|
|
131
|
+
i < skeletonContentLines - 1 ? "mb-2 w-full" : "w-4/5"
|
|
132
|
+
)
|
|
133
|
+
},
|
|
134
|
+
i
|
|
135
|
+
)) }),
|
|
136
|
+
skeletonHasFooter && /* @__PURE__ */ jsx(CardFooter, { children: /* @__PURE__ */ jsx(View, { className: "h-9 w-24 bg-interactive-disabled rounded" }) })
|
|
137
|
+
] }) : children;
|
|
138
|
+
const baseClassName = cn(
|
|
139
|
+
"rounded-lg overflow-hidden relative",
|
|
140
|
+
// Apply variant styles, but only use default border color if no custom borderColor
|
|
141
|
+
variant === "outline" ? borderColor ? "border-2" : variantStyles[variant] : variantStyles[variant],
|
|
142
|
+
// Remove Tailwind shadow classes - we're using elevation shadows via style prop
|
|
143
|
+
isClickable && "web:cursor-pointer web:transition-all web:duration-150",
|
|
144
|
+
isClickable && "web:hover:-translate-y-0.5 active:scale-[0.99]",
|
|
145
|
+
isLoading && "pointer-events-none animate-pulse",
|
|
146
|
+
className
|
|
147
|
+
);
|
|
148
|
+
const accentStyle = useMemo(() => variant === "accent" ? {
|
|
149
|
+
borderLeftWidth: accentWidth ?? 3,
|
|
150
|
+
borderLeftColor: accentColor ?? "var(--color-brand-primary)"
|
|
151
|
+
} : {}, [variant, accentWidth, accentColor]);
|
|
152
|
+
const mergedStyle = useMemo(() => {
|
|
153
|
+
const elevationStyle = {
|
|
154
|
+
backgroundColor: bgColor || surfaceColor,
|
|
155
|
+
borderRadius: 8,
|
|
156
|
+
overflow: "hidden",
|
|
157
|
+
...shadowStyle,
|
|
158
|
+
...accentStyle
|
|
159
|
+
};
|
|
160
|
+
if (borderColor) {
|
|
161
|
+
elevationStyle.borderColor = borderColor;
|
|
162
|
+
}
|
|
163
|
+
if (!style) return elevationStyle;
|
|
164
|
+
return [elevationStyle, style];
|
|
165
|
+
}, [style, surfaceColor, shadowStyle, borderColor, bgColor, accentStyle]);
|
|
166
|
+
if (isClickable) {
|
|
167
|
+
return /* @__PURE__ */ jsx(
|
|
168
|
+
Pressable,
|
|
169
|
+
{
|
|
170
|
+
onPress,
|
|
171
|
+
onHoverIn: () => setIsHovered(true),
|
|
172
|
+
onHoverOut: () => setIsHovered(false),
|
|
173
|
+
accessibilityRole: "button",
|
|
174
|
+
className: baseClassName,
|
|
175
|
+
style: mergedStyle,
|
|
176
|
+
...props,
|
|
177
|
+
children: cardContent
|
|
178
|
+
}
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
return /* @__PURE__ */ jsx(View, { className: baseClassName, style: mergedStyle, ...props, children: cardContent });
|
|
182
|
+
}
|
|
183
|
+
function CardHeader({ children, className }) {
|
|
184
|
+
return /* @__PURE__ */ jsx(View, { className: cn("px-6 py-6", className), children });
|
|
185
|
+
}
|
|
186
|
+
function CardTitle({ children, className }) {
|
|
187
|
+
return /* @__PURE__ */ jsx(
|
|
188
|
+
Text,
|
|
189
|
+
{
|
|
190
|
+
accessibilityRole: "header",
|
|
191
|
+
className: cn("text-lg font-semibold text-text-primary font-heading", className),
|
|
192
|
+
children
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
function CardDescription({ children, className }) {
|
|
197
|
+
return /* @__PURE__ */ jsx(Text, { className: cn("mt-1.5 text-sm text-text-secondary", className), children });
|
|
198
|
+
}
|
|
199
|
+
function CardContent({ children, className }) {
|
|
200
|
+
return /* @__PURE__ */ jsx(View, { className: cn("px-6 py-4", className), children });
|
|
201
|
+
}
|
|
202
|
+
function CardFooter({ children, className }) {
|
|
203
|
+
return /* @__PURE__ */ jsx(View, { className: cn("px-6 py-4 flex-row items-center gap-2", className), children });
|
|
204
|
+
}
|
|
205
|
+
function CardSkeleton({
|
|
206
|
+
hasHeader = true,
|
|
207
|
+
hasFooter = false,
|
|
208
|
+
contentLines = 3,
|
|
209
|
+
className
|
|
210
|
+
}) {
|
|
211
|
+
return /* @__PURE__ */ jsxs(Card, { variant: "elevated", elevation: 2, className: cn("animate-pulse", className), children: [
|
|
212
|
+
hasHeader && /* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
213
|
+
/* @__PURE__ */ jsx(View, { className: "h-5 w-1/3 bg-interactive-disabled rounded" }),
|
|
214
|
+
/* @__PURE__ */ jsx(View, { className: "mt-2 h-4 w-2/3 bg-interactive-disabled rounded" })
|
|
215
|
+
] }),
|
|
216
|
+
/* @__PURE__ */ jsx(CardContent, { children: Array.from({ length: contentLines }).map((_, i) => /* @__PURE__ */ jsx(
|
|
217
|
+
View,
|
|
218
|
+
{
|
|
219
|
+
className: cn(
|
|
220
|
+
"h-4 bg-interactive-disabled rounded",
|
|
221
|
+
i < contentLines - 1 ? "mb-2 w-full" : "w-4/5"
|
|
222
|
+
)
|
|
223
|
+
},
|
|
224
|
+
i
|
|
225
|
+
)) }),
|
|
226
|
+
hasFooter && /* @__PURE__ */ jsx(CardFooter, { children: /* @__PURE__ */ jsx(View, { className: "h-9 w-24 bg-interactive-disabled rounded" }) })
|
|
227
|
+
] });
|
|
228
|
+
}
|
|
229
|
+
var sizeStyles = {
|
|
230
|
+
xs: "w-1 h-1",
|
|
231
|
+
sm: "w-1.5 h-1.5",
|
|
232
|
+
md: "w-2 h-2",
|
|
233
|
+
lg: "w-2.5 h-2.5"
|
|
234
|
+
};
|
|
235
|
+
var colorStyles = {
|
|
236
|
+
default: "bg-text-tertiary",
|
|
237
|
+
primary: "bg-brand-primary",
|
|
238
|
+
success: "bg-status-success",
|
|
239
|
+
error: "bg-status-error",
|
|
240
|
+
warning: "bg-status-warning",
|
|
241
|
+
info: "bg-status-info"
|
|
242
|
+
};
|
|
243
|
+
function Indicator({
|
|
244
|
+
size = "sm",
|
|
245
|
+
color = "default",
|
|
246
|
+
customColor,
|
|
247
|
+
glow = false,
|
|
248
|
+
ring = false,
|
|
249
|
+
className,
|
|
250
|
+
style,
|
|
251
|
+
...props
|
|
252
|
+
}) {
|
|
253
|
+
const dynamicStyle = {
|
|
254
|
+
...typeof style === "object" ? style : {},
|
|
255
|
+
...customColor ? { backgroundColor: customColor } : {},
|
|
256
|
+
...glow && !customColor ? {} : {},
|
|
257
|
+
...glow && customColor ? { boxShadow: `0 0 6px ${customColor}` } : {}
|
|
258
|
+
};
|
|
259
|
+
return /* @__PURE__ */ jsx(
|
|
260
|
+
View,
|
|
261
|
+
{
|
|
262
|
+
className: cn(
|
|
263
|
+
"rounded-full shrink-0",
|
|
264
|
+
sizeStyles[size],
|
|
265
|
+
!customColor && colorStyles[color],
|
|
266
|
+
ring && "border-2 border-background-base",
|
|
267
|
+
glow && !customColor && color === "success" && "shadow-[0_0_6px_rgba(20,184,166,0.6)]",
|
|
268
|
+
glow && !customColor && color === "error" && "shadow-[0_0_6px_rgba(239,68,68,0.6)]",
|
|
269
|
+
glow && !customColor && color === "warning" && "shadow-[0_0_6px_rgba(245,158,11,0.6)]",
|
|
270
|
+
glow && !customColor && color === "primary" && "shadow-[0_0_6px_rgba(255,121,0,0.6)]",
|
|
271
|
+
className
|
|
272
|
+
),
|
|
273
|
+
style: Object.keys(dynamicStyle).length > 0 ? dynamicStyle : style,
|
|
274
|
+
...props
|
|
275
|
+
}
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
var variantColorStyles = {
|
|
279
|
+
solid: {
|
|
280
|
+
default: "bg-border-strong text-text-inverse",
|
|
281
|
+
primary: "bg-brand-primary text-text-inverse",
|
|
282
|
+
secondary: "bg-brand-secondary text-text-inverse",
|
|
283
|
+
success: "bg-status-success text-text-inverse",
|
|
284
|
+
error: "bg-status-error text-text-inverse",
|
|
285
|
+
warning: "bg-status-warning text-text-inverse",
|
|
286
|
+
info: "bg-status-info text-text-inverse"
|
|
287
|
+
},
|
|
288
|
+
subtle: {
|
|
289
|
+
default: "bg-surface-raised text-text-secondary",
|
|
290
|
+
primary: "bg-brand-primary-subtle text-brand-primary",
|
|
291
|
+
secondary: "bg-brand-secondary-subtle text-brand-secondary",
|
|
292
|
+
success: "bg-status-success-subtle text-status-success",
|
|
293
|
+
error: "bg-status-error-subtle text-status-error",
|
|
294
|
+
warning: "bg-status-warning-subtle text-status-warning",
|
|
295
|
+
info: "bg-status-info-subtle text-status-info"
|
|
296
|
+
},
|
|
297
|
+
outline: {
|
|
298
|
+
default: "border border-border-strong text-text-secondary",
|
|
299
|
+
primary: "border border-brand-primary text-brand-primary",
|
|
300
|
+
secondary: "border border-brand-secondary text-brand-secondary",
|
|
301
|
+
success: "border border-status-success text-status-success",
|
|
302
|
+
error: "border border-status-error text-status-error",
|
|
303
|
+
warning: "border border-status-warning text-status-warning",
|
|
304
|
+
info: "border border-status-info text-status-info"
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
var sizeStyles2 = {
|
|
308
|
+
sm: "px-1.5 py-0.5 text-xs",
|
|
309
|
+
md: "px-2 py-0.5 text-xs",
|
|
310
|
+
lg: "px-2.5 py-1 text-sm"
|
|
311
|
+
};
|
|
312
|
+
function Badge({
|
|
313
|
+
variant = "subtle",
|
|
314
|
+
color = "default",
|
|
315
|
+
size = "md",
|
|
316
|
+
dot,
|
|
317
|
+
dotColor,
|
|
318
|
+
className,
|
|
319
|
+
children,
|
|
320
|
+
...props
|
|
321
|
+
}) {
|
|
322
|
+
return /* @__PURE__ */ jsxs(
|
|
323
|
+
View,
|
|
324
|
+
{
|
|
325
|
+
className: cn(
|
|
326
|
+
"inline-flex items-center justify-center rounded-full font-medium",
|
|
327
|
+
variantColorStyles[variant][color],
|
|
328
|
+
sizeStyles2[size],
|
|
329
|
+
className
|
|
330
|
+
),
|
|
331
|
+
...props,
|
|
332
|
+
children: [
|
|
333
|
+
dot && /* @__PURE__ */ jsx(
|
|
334
|
+
Indicator,
|
|
335
|
+
{
|
|
336
|
+
size: "xs",
|
|
337
|
+
color: dotColor ?? (color === "default" || color === "secondary" ? "default" : color),
|
|
338
|
+
className: "mr-0.5"
|
|
339
|
+
}
|
|
340
|
+
),
|
|
341
|
+
typeof children === "string" ? /* @__PURE__ */ jsx(Text, { className: "text-inherit font-medium", children }) : children
|
|
342
|
+
]
|
|
343
|
+
}
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
function BadgeText({ children, className }) {
|
|
347
|
+
return /* @__PURE__ */ jsx(Text, { className: cn("text-inherit font-medium", className), children });
|
|
348
|
+
}
|
|
349
|
+
var solidVariantColors = {
|
|
350
|
+
success: "#14B8A6",
|
|
351
|
+
warning: "#FFB020",
|
|
352
|
+
error: "#D14343",
|
|
353
|
+
neutral: "#6B7280"
|
|
354
|
+
};
|
|
355
|
+
var ringVariantStyles = {
|
|
356
|
+
"on-track": {
|
|
357
|
+
backgroundColor: "rgba(20,184,166,0.15)",
|
|
358
|
+
borderWidth: 1,
|
|
359
|
+
borderColor: "rgba(20,184,166,0.3)"
|
|
360
|
+
},
|
|
361
|
+
deviation: {
|
|
362
|
+
backgroundColor: "rgba(245,158,11,0.15)",
|
|
363
|
+
borderWidth: 1,
|
|
364
|
+
borderColor: "rgba(245,158,11,0.25)"
|
|
365
|
+
},
|
|
366
|
+
future: {
|
|
367
|
+
backgroundColor: "rgba(107,114,128,0.1)",
|
|
368
|
+
borderWidth: 1,
|
|
369
|
+
borderStyle: "dashed",
|
|
370
|
+
borderColor: "rgba(107,114,128,0.2)"
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
var glowStyles = {
|
|
374
|
+
success: { boxShadow: "0 0 4px rgba(20,184,166,0.4)" },
|
|
375
|
+
warning: { boxShadow: "0 0 4px rgba(245,158,11,0.4)" },
|
|
376
|
+
error: { boxShadow: "0 0 4px rgba(239,68,68,0.4)" },
|
|
377
|
+
neutral: { boxShadow: "0 0 4px rgba(107,114,128,0.4)" },
|
|
378
|
+
"on-track": { boxShadow: "0 0 4px rgba(20,184,166,0.4)" },
|
|
379
|
+
deviation: { boxShadow: "0 0 4px rgba(245,158,11,0.4)" },
|
|
380
|
+
future: { boxShadow: "0 0 4px rgba(107,114,128,0.4)" }
|
|
381
|
+
};
|
|
382
|
+
var iconChars = {
|
|
383
|
+
check: "\u2713",
|
|
384
|
+
exclamation: "!",
|
|
385
|
+
dash: "\u2014"
|
|
386
|
+
};
|
|
387
|
+
var iconColors = {
|
|
388
|
+
success: "#0A5C52",
|
|
389
|
+
warning: "#6B4000",
|
|
390
|
+
error: "#5C1A1A",
|
|
391
|
+
neutral: "#D1D5DB",
|
|
392
|
+
"on-track": "#14B8A6",
|
|
393
|
+
deviation: "#FFB020",
|
|
394
|
+
future: "#6B7280"
|
|
395
|
+
};
|
|
396
|
+
var sizeDimensions = {
|
|
397
|
+
sm: { size: 8, showIcon: false },
|
|
398
|
+
md: { size: 18, showIcon: true }
|
|
399
|
+
};
|
|
400
|
+
function isSolidVariant(variant) {
|
|
401
|
+
return variant in solidVariantColors;
|
|
402
|
+
}
|
|
403
|
+
function StatusDot({
|
|
404
|
+
variant,
|
|
405
|
+
icon,
|
|
406
|
+
size = "sm",
|
|
407
|
+
glow,
|
|
408
|
+
label,
|
|
409
|
+
className,
|
|
410
|
+
...props
|
|
411
|
+
}) {
|
|
412
|
+
const config = sizeDimensions[size];
|
|
413
|
+
const solid = isSolidVariant(variant);
|
|
414
|
+
const ring = ringVariantStyles[variant];
|
|
415
|
+
const glowStyle = glow ? glowStyles[variant] : null;
|
|
416
|
+
const dot = /* @__PURE__ */ jsx(
|
|
417
|
+
View,
|
|
418
|
+
{
|
|
419
|
+
style: [
|
|
420
|
+
{
|
|
421
|
+
width: config.size,
|
|
422
|
+
height: config.size,
|
|
423
|
+
borderRadius: 9999,
|
|
424
|
+
alignItems: "center",
|
|
425
|
+
justifyContent: "center",
|
|
426
|
+
flexShrink: 0
|
|
427
|
+
},
|
|
428
|
+
solid ? { backgroundColor: solidVariantColors[variant] } : void 0,
|
|
429
|
+
ring ? ring : void 0,
|
|
430
|
+
glowStyle ? glowStyle : void 0
|
|
431
|
+
],
|
|
432
|
+
accessibilityRole: "image",
|
|
433
|
+
accessibilityLabel: `${variant} status`,
|
|
434
|
+
testID: "status-dot",
|
|
435
|
+
children: config.showIcon && icon && /* @__PURE__ */ jsx(
|
|
436
|
+
Text,
|
|
437
|
+
{
|
|
438
|
+
style: {
|
|
439
|
+
fontSize: 11,
|
|
440
|
+
lineHeight: 11,
|
|
441
|
+
fontWeight: "900",
|
|
442
|
+
color: iconColors[variant]
|
|
443
|
+
},
|
|
444
|
+
accessibilityElementsHidden: true,
|
|
445
|
+
children: iconChars[icon]
|
|
446
|
+
}
|
|
447
|
+
)
|
|
448
|
+
}
|
|
449
|
+
);
|
|
450
|
+
if (label) {
|
|
451
|
+
return /* @__PURE__ */ jsxs(
|
|
452
|
+
View,
|
|
453
|
+
{
|
|
454
|
+
className,
|
|
455
|
+
style: { flexDirection: "row", alignItems: "center", gap: 6 },
|
|
456
|
+
accessibilityLabel: `${variant} status: ${label}`,
|
|
457
|
+
...props,
|
|
458
|
+
children: [
|
|
459
|
+
/* @__PURE__ */ jsx(View, { accessibilityElementsHidden: true, children: dot }),
|
|
460
|
+
/* @__PURE__ */ jsx(
|
|
461
|
+
Text,
|
|
462
|
+
{
|
|
463
|
+
style: { fontSize: 12, lineHeight: 16, color: "#9CA3AF" },
|
|
464
|
+
accessibilityElementsHidden: true,
|
|
465
|
+
children: label
|
|
466
|
+
}
|
|
467
|
+
)
|
|
468
|
+
]
|
|
469
|
+
}
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
return /* @__PURE__ */ jsx(View, { className, ...props, children: dot });
|
|
473
|
+
}
|
|
474
|
+
var BRAND_PRIMARY = "#FF7900";
|
|
475
|
+
function normalizeData(data, height) {
|
|
476
|
+
if (data.length === 0) return [];
|
|
477
|
+
const min = Math.min(...data);
|
|
478
|
+
const max = Math.max(...data);
|
|
479
|
+
const range = max - min || 1;
|
|
480
|
+
return data.map((v) => height - (v - min) / range * height);
|
|
481
|
+
}
|
|
482
|
+
function normalizeValue(value, data, height) {
|
|
483
|
+
const min = Math.min(...data);
|
|
484
|
+
const max = Math.max(...data);
|
|
485
|
+
const range = max - min || 1;
|
|
486
|
+
return height - (value - min) / range * height;
|
|
487
|
+
}
|
|
488
|
+
function Sparkline({
|
|
489
|
+
data,
|
|
490
|
+
width = 80,
|
|
491
|
+
height = 30,
|
|
492
|
+
color,
|
|
493
|
+
showDots = false,
|
|
494
|
+
referenceLines,
|
|
495
|
+
highlightLast = false,
|
|
496
|
+
className,
|
|
497
|
+
...props
|
|
498
|
+
}) {
|
|
499
|
+
if (data.length === 0) {
|
|
500
|
+
return /* @__PURE__ */ jsx(
|
|
501
|
+
View,
|
|
502
|
+
{
|
|
503
|
+
style: { width, height },
|
|
504
|
+
className,
|
|
505
|
+
testID: "sparkline-empty",
|
|
506
|
+
accessibilityLabel: "Sparkline chart, no data",
|
|
507
|
+
...props
|
|
508
|
+
}
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
const resolvedColor = color ?? BRAND_PRIMARY;
|
|
512
|
+
const normalized = normalizeData(data, height);
|
|
513
|
+
const stepX = data.length > 1 ? width / (data.length - 1) : 0;
|
|
514
|
+
const dotSize = 3;
|
|
515
|
+
const highlightSize = 6;
|
|
516
|
+
return /* @__PURE__ */ jsxs(
|
|
517
|
+
View,
|
|
518
|
+
{
|
|
519
|
+
style: { width, height, position: "relative" },
|
|
520
|
+
className: cn(className),
|
|
521
|
+
accessibilityRole: "image",
|
|
522
|
+
accessibilityLabel: `Sparkline chart with ${data.length} data points`,
|
|
523
|
+
testID: "sparkline",
|
|
524
|
+
...props,
|
|
525
|
+
children: [
|
|
526
|
+
referenceLines?.map((line, i) => {
|
|
527
|
+
const y = normalizeValue(line.value, data, height);
|
|
528
|
+
return /* @__PURE__ */ jsx(
|
|
529
|
+
View,
|
|
530
|
+
{
|
|
531
|
+
style: [
|
|
532
|
+
{
|
|
533
|
+
position: "absolute",
|
|
534
|
+
top: y,
|
|
535
|
+
left: 0,
|
|
536
|
+
right: 0,
|
|
537
|
+
opacity: 0.6
|
|
538
|
+
},
|
|
539
|
+
line.dashed ? {
|
|
540
|
+
height: 0,
|
|
541
|
+
borderStyle: "dashed",
|
|
542
|
+
borderTopWidth: 1,
|
|
543
|
+
borderTopColor: line.color
|
|
544
|
+
} : {
|
|
545
|
+
height: 1,
|
|
546
|
+
backgroundColor: line.color
|
|
547
|
+
}
|
|
548
|
+
],
|
|
549
|
+
accessibilityElementsHidden: true,
|
|
550
|
+
testID: `sparkline-reference-${i}`,
|
|
551
|
+
children: line.label && /* @__PURE__ */ jsx(
|
|
552
|
+
Text,
|
|
553
|
+
{
|
|
554
|
+
style: {
|
|
555
|
+
position: "absolute",
|
|
556
|
+
right: 0,
|
|
557
|
+
top: -10,
|
|
558
|
+
fontSize: 7,
|
|
559
|
+
color: line.color,
|
|
560
|
+
opacity: 1
|
|
561
|
+
},
|
|
562
|
+
testID: `sparkline-reference-label-${i}`,
|
|
563
|
+
children: line.label
|
|
564
|
+
}
|
|
565
|
+
)
|
|
566
|
+
},
|
|
567
|
+
`ref-${i}`
|
|
568
|
+
);
|
|
569
|
+
}),
|
|
570
|
+
normalized.map((y, i) => {
|
|
571
|
+
if (i === 0) return null;
|
|
572
|
+
const prevY = normalized[i - 1];
|
|
573
|
+
const x1 = (i - 1) * stepX;
|
|
574
|
+
const x2 = i * stepX;
|
|
575
|
+
const dx = x2 - x1;
|
|
576
|
+
const dy = y - prevY;
|
|
577
|
+
const length = Math.sqrt(dx * dx + dy * dy);
|
|
578
|
+
const angle = Math.atan2(dy, dx) * (180 / Math.PI);
|
|
579
|
+
return /* @__PURE__ */ jsx(
|
|
580
|
+
View,
|
|
581
|
+
{
|
|
582
|
+
style: {
|
|
583
|
+
position: "absolute",
|
|
584
|
+
left: x1,
|
|
585
|
+
top: prevY,
|
|
586
|
+
width: length,
|
|
587
|
+
height: 1.5,
|
|
588
|
+
backgroundColor: resolvedColor,
|
|
589
|
+
transformOrigin: "0 0",
|
|
590
|
+
transform: [{ rotate: `${angle}deg` }]
|
|
591
|
+
},
|
|
592
|
+
accessibilityElementsHidden: true,
|
|
593
|
+
testID: `sparkline-segment-${i}`
|
|
594
|
+
},
|
|
595
|
+
`line-${i}`
|
|
596
|
+
);
|
|
597
|
+
}),
|
|
598
|
+
(showDots || highlightLast) && normalized.map((y, i) => {
|
|
599
|
+
const isLast = i === data.length - 1;
|
|
600
|
+
const shouldShow = showDots || highlightLast && isLast;
|
|
601
|
+
if (!shouldShow) return null;
|
|
602
|
+
const size = highlightLast && isLast ? highlightSize : dotSize;
|
|
603
|
+
return /* @__PURE__ */ jsx(
|
|
604
|
+
View,
|
|
605
|
+
{
|
|
606
|
+
style: {
|
|
607
|
+
position: "absolute",
|
|
608
|
+
left: i * stepX - size / 2,
|
|
609
|
+
top: y - size / 2,
|
|
610
|
+
width: size,
|
|
611
|
+
height: size,
|
|
612
|
+
borderRadius: size / 2,
|
|
613
|
+
backgroundColor: resolvedColor
|
|
614
|
+
},
|
|
615
|
+
accessibilityElementsHidden: true,
|
|
616
|
+
testID: `sparkline-dot-${i}`
|
|
617
|
+
},
|
|
618
|
+
`dot-${i}`
|
|
619
|
+
);
|
|
620
|
+
})
|
|
621
|
+
]
|
|
622
|
+
}
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
var BRAND_PRIMARY2 = "#FF7900";
|
|
626
|
+
var BRAND_PRIMARY_DARK = "#C45E00";
|
|
627
|
+
var BRAND_PRIMARY_LIGHT = "#FFB066";
|
|
628
|
+
var TEXT_PRIMARY = "var(--color-text-primary)";
|
|
629
|
+
var TEXT_SECONDARY = "var(--color-text-secondary)";
|
|
630
|
+
var TEXT_TERTIARY = "var(--color-text-tertiary)";
|
|
631
|
+
var SUCCESS = "#14B8A6";
|
|
632
|
+
var WARNING = "#FFB020";
|
|
633
|
+
var ERROR = "#D14343";
|
|
634
|
+
var ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY2, BRAND_PRIMARY_LIGHT];
|
|
635
|
+
var CARD_GRADIENT = "linear-gradient(135deg, var(--color-surface-elevated) 0%, var(--color-surface-raised) 100%)";
|
|
636
|
+
var GAUGE_GRADIENT = "linear-gradient(90deg, rgba(20,184,166,0.25) 0%, rgba(255,176,32,0.25) 50%, rgba(209,67,67,0.25) 100%)";
|
|
637
|
+
var STATUS_VARIANTS = {
|
|
638
|
+
success: { bg: "rgba(20,184,166,0.15)", border: "rgba(20,184,166,0.3)", text: SUCCESS },
|
|
639
|
+
warning: { bg: "rgba(255,176,32,0.15)", border: "rgba(255,176,32,0.3)", text: WARNING },
|
|
640
|
+
error: { bg: "rgba(209,67,67,0.15)", border: "rgba(209,67,67,0.25)", text: ERROR }
|
|
641
|
+
};
|
|
642
|
+
function clamp01(value) {
|
|
643
|
+
if (!Number.isFinite(value)) return 0;
|
|
644
|
+
if (value < 0) return 0;
|
|
645
|
+
if (value > 1) return 1;
|
|
646
|
+
return value;
|
|
647
|
+
}
|
|
648
|
+
function getGaugeZoneColor(level) {
|
|
649
|
+
if (level >= 0.7) return ERROR;
|
|
650
|
+
if (level >= 0.4) return WARNING;
|
|
651
|
+
return SUCCESS;
|
|
652
|
+
}
|
|
653
|
+
function renderCoachingText(text, highlights) {
|
|
654
|
+
if (!highlights || highlights.length === 0) return text;
|
|
655
|
+
const escaped = highlights.filter(Boolean).map((h) => h.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
656
|
+
if (escaped.length === 0) return text;
|
|
657
|
+
const regex = new RegExp(`(${escaped.join("|")})`, "g");
|
|
658
|
+
return text.split(regex).map(
|
|
659
|
+
(part, index) => highlights.includes(part) ? /* @__PURE__ */ jsx(
|
|
660
|
+
Text,
|
|
661
|
+
{
|
|
662
|
+
style: { fontWeight: "700", color: TEXT_PRIMARY },
|
|
663
|
+
children: part
|
|
664
|
+
},
|
|
665
|
+
`${part}-${index}`
|
|
666
|
+
) : /* @__PURE__ */ jsx(Fragment$1, { children: part }, `t-${index}`)
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
function StatusPill({ badge }) {
|
|
670
|
+
const colors = STATUS_VARIANTS[badge.variant];
|
|
671
|
+
return /* @__PURE__ */ jsxs(
|
|
672
|
+
View,
|
|
673
|
+
{
|
|
674
|
+
className: "flex-row items-center",
|
|
675
|
+
style: {
|
|
676
|
+
gap: 5,
|
|
677
|
+
paddingVertical: 3,
|
|
678
|
+
paddingHorizontal: 8,
|
|
679
|
+
borderRadius: 4,
|
|
680
|
+
borderWidth: 1,
|
|
681
|
+
backgroundColor: colors.bg,
|
|
682
|
+
borderColor: colors.border
|
|
683
|
+
},
|
|
684
|
+
testID: "meso-status-card-badge",
|
|
685
|
+
children: [
|
|
686
|
+
/* @__PURE__ */ jsx(View, { accessibilityElementsHidden: true, children: /* @__PURE__ */ jsx(StatusDot, { variant: badge.variant, size: "sm" }) }),
|
|
687
|
+
/* @__PURE__ */ jsx(
|
|
688
|
+
Text,
|
|
689
|
+
{
|
|
690
|
+
style: {
|
|
691
|
+
fontSize: 10,
|
|
692
|
+
fontFamily: "Inter, sans-serif",
|
|
693
|
+
fontWeight: "700",
|
|
694
|
+
letterSpacing: 0.3,
|
|
695
|
+
color: colors.text
|
|
696
|
+
},
|
|
697
|
+
children: badge.label
|
|
698
|
+
}
|
|
699
|
+
)
|
|
700
|
+
]
|
|
701
|
+
}
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
function MetricCell({ metric }) {
|
|
705
|
+
return /* @__PURE__ */ jsxs(View, { style: { width: "48%" }, testID: "meso-status-card-metric", children: [
|
|
706
|
+
/* @__PURE__ */ jsx(
|
|
707
|
+
Text,
|
|
708
|
+
{
|
|
709
|
+
style: {
|
|
710
|
+
fontSize: 10,
|
|
711
|
+
fontFamily: "Inter, sans-serif",
|
|
712
|
+
fontWeight: "600",
|
|
713
|
+
letterSpacing: 0.5,
|
|
714
|
+
textTransform: "uppercase",
|
|
715
|
+
color: TEXT_TERTIARY
|
|
716
|
+
},
|
|
717
|
+
children: metric.label
|
|
718
|
+
}
|
|
719
|
+
),
|
|
720
|
+
/* @__PURE__ */ jsx(
|
|
721
|
+
Text,
|
|
722
|
+
{
|
|
723
|
+
style: {
|
|
724
|
+
marginTop: 2,
|
|
725
|
+
fontSize: 13,
|
|
726
|
+
fontFamily: "Inter, sans-serif",
|
|
727
|
+
fontWeight: "600",
|
|
728
|
+
color: TEXT_PRIMARY
|
|
729
|
+
},
|
|
730
|
+
children: metric.value
|
|
731
|
+
}
|
|
732
|
+
)
|
|
733
|
+
] });
|
|
734
|
+
}
|
|
735
|
+
function Gauge({ gauge }) {
|
|
736
|
+
const level = clamp01(gauge.level);
|
|
737
|
+
const percentage = Math.round(level * 100);
|
|
738
|
+
const markerColor = getGaugeZoneColor(level);
|
|
739
|
+
return /* @__PURE__ */ jsxs(
|
|
740
|
+
View,
|
|
741
|
+
{
|
|
742
|
+
style: { gap: 6 },
|
|
743
|
+
accessibilityRole: "progressbar",
|
|
744
|
+
accessibilityValue: { min: 0, max: 100, now: percentage },
|
|
745
|
+
accessibilityLabel: `${gauge.label}: ${percentage}%`,
|
|
746
|
+
"aria-valuenow": percentage,
|
|
747
|
+
"aria-valuemin": 0,
|
|
748
|
+
"aria-valuemax": 100,
|
|
749
|
+
testID: "meso-status-card-gauge",
|
|
750
|
+
children: [
|
|
751
|
+
/* @__PURE__ */ jsxs(
|
|
752
|
+
View,
|
|
753
|
+
{
|
|
754
|
+
className: "flex-row items-center justify-between",
|
|
755
|
+
accessibilityElementsHidden: true,
|
|
756
|
+
children: [
|
|
757
|
+
/* @__PURE__ */ jsx(
|
|
758
|
+
Text,
|
|
759
|
+
{
|
|
760
|
+
style: {
|
|
761
|
+
fontSize: 10,
|
|
762
|
+
fontFamily: "Inter, sans-serif",
|
|
763
|
+
fontWeight: "500",
|
|
764
|
+
letterSpacing: 0.5,
|
|
765
|
+
textTransform: "uppercase",
|
|
766
|
+
color: TEXT_SECONDARY
|
|
767
|
+
},
|
|
768
|
+
children: gauge.label
|
|
769
|
+
}
|
|
770
|
+
),
|
|
771
|
+
gauge.sublabel != null && /* @__PURE__ */ jsx(
|
|
772
|
+
Text,
|
|
773
|
+
{
|
|
774
|
+
style: {
|
|
775
|
+
fontSize: 10,
|
|
776
|
+
fontFamily: "Inter, sans-serif",
|
|
777
|
+
fontWeight: "500",
|
|
778
|
+
color: TEXT_TERTIARY
|
|
779
|
+
},
|
|
780
|
+
children: gauge.sublabel
|
|
781
|
+
}
|
|
782
|
+
)
|
|
783
|
+
]
|
|
784
|
+
}
|
|
785
|
+
),
|
|
786
|
+
/* @__PURE__ */ jsxs(
|
|
787
|
+
View,
|
|
788
|
+
{
|
|
789
|
+
style: {
|
|
790
|
+
height: 8,
|
|
791
|
+
borderRadius: 4,
|
|
792
|
+
position: "relative",
|
|
793
|
+
backgroundImage: GAUGE_GRADIENT
|
|
794
|
+
},
|
|
795
|
+
accessibilityElementsHidden: true,
|
|
796
|
+
children: [
|
|
797
|
+
/* @__PURE__ */ jsx(
|
|
798
|
+
View,
|
|
799
|
+
{
|
|
800
|
+
style: {
|
|
801
|
+
position: "absolute",
|
|
802
|
+
left: "50%",
|
|
803
|
+
top: 0,
|
|
804
|
+
bottom: 0,
|
|
805
|
+
width: 1,
|
|
806
|
+
marginLeft: -0.5,
|
|
807
|
+
backgroundColor: "rgba(255,255,255,0.3)"
|
|
808
|
+
},
|
|
809
|
+
testID: "meso-status-card-gauge-center"
|
|
810
|
+
}
|
|
811
|
+
),
|
|
812
|
+
/* @__PURE__ */ jsx(
|
|
813
|
+
View,
|
|
814
|
+
{
|
|
815
|
+
style: {
|
|
816
|
+
position: "absolute",
|
|
817
|
+
left: `${percentage}%`,
|
|
818
|
+
top: -3,
|
|
819
|
+
marginLeft: -7,
|
|
820
|
+
width: 14,
|
|
821
|
+
height: 14,
|
|
822
|
+
borderRadius: 9999,
|
|
823
|
+
borderWidth: 2,
|
|
824
|
+
borderColor: TEXT_PRIMARY,
|
|
825
|
+
backgroundColor: markerColor,
|
|
826
|
+
boxShadow: "0 0 4px rgba(0,0,0,0.5)"
|
|
827
|
+
},
|
|
828
|
+
testID: "meso-status-card-gauge-marker"
|
|
829
|
+
}
|
|
830
|
+
)
|
|
831
|
+
]
|
|
832
|
+
}
|
|
833
|
+
)
|
|
834
|
+
]
|
|
835
|
+
}
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
function MesoStatusCard({
|
|
839
|
+
mesoName,
|
|
840
|
+
mesoSubtitle,
|
|
841
|
+
statusBadge,
|
|
842
|
+
metrics,
|
|
843
|
+
gauges,
|
|
844
|
+
coaching,
|
|
845
|
+
nextTarget,
|
|
846
|
+
className,
|
|
847
|
+
...props
|
|
848
|
+
}) {
|
|
849
|
+
return /* @__PURE__ */ jsxs(
|
|
850
|
+
Card,
|
|
851
|
+
{
|
|
852
|
+
variant: "outline",
|
|
853
|
+
elevation: 1,
|
|
854
|
+
borderColor: BRAND_PRIMARY_DARK,
|
|
855
|
+
className,
|
|
856
|
+
style: {
|
|
857
|
+
borderColor: BRAND_PRIMARY_DARK,
|
|
858
|
+
borderWidth: 1,
|
|
859
|
+
backgroundImage: CARD_GRADIENT
|
|
860
|
+
},
|
|
861
|
+
role: "article",
|
|
862
|
+
"aria-label": `Mesocycle status for ${mesoName}`,
|
|
863
|
+
testID: "meso-status-card",
|
|
864
|
+
...props,
|
|
865
|
+
children: [
|
|
866
|
+
/* @__PURE__ */ jsx(
|
|
867
|
+
View,
|
|
868
|
+
{
|
|
869
|
+
className: "flex-row",
|
|
870
|
+
style: { position: "absolute", top: 0, left: 0, right: 0, height: 3, zIndex: 1 },
|
|
871
|
+
accessibilityElementsHidden: true,
|
|
872
|
+
testID: "meso-status-card-accent",
|
|
873
|
+
children: ACCENT_STOPS.map((color) => /* @__PURE__ */ jsx(View, { style: { flex: 1, backgroundColor: color } }, color))
|
|
874
|
+
}
|
|
875
|
+
),
|
|
876
|
+
/* @__PURE__ */ jsxs(View, { style: { padding: 14, gap: 14 }, testID: "meso-status-card-body", children: [
|
|
877
|
+
/* @__PURE__ */ jsxs(View, { testID: "meso-status-card-header", children: [
|
|
878
|
+
/* @__PURE__ */ jsxs(
|
|
879
|
+
View,
|
|
880
|
+
{
|
|
881
|
+
className: "flex-row items-center justify-between",
|
|
882
|
+
style: { gap: 8 },
|
|
883
|
+
children: [
|
|
884
|
+
/* @__PURE__ */ jsx(
|
|
885
|
+
Text,
|
|
886
|
+
{
|
|
887
|
+
style: {
|
|
888
|
+
flexShrink: 1,
|
|
889
|
+
fontSize: 15,
|
|
890
|
+
fontFamily: '"Space Grotesk", sans-serif',
|
|
891
|
+
fontWeight: "700",
|
|
892
|
+
color: TEXT_PRIMARY
|
|
893
|
+
},
|
|
894
|
+
testID: "meso-status-card-name",
|
|
895
|
+
children: mesoName
|
|
896
|
+
}
|
|
897
|
+
),
|
|
898
|
+
/* @__PURE__ */ jsx(StatusPill, { badge: statusBadge })
|
|
899
|
+
]
|
|
900
|
+
}
|
|
901
|
+
),
|
|
902
|
+
/* @__PURE__ */ jsx(
|
|
903
|
+
Text,
|
|
904
|
+
{
|
|
905
|
+
style: {
|
|
906
|
+
marginTop: 4,
|
|
907
|
+
fontSize: 12,
|
|
908
|
+
fontFamily: "Inter, sans-serif",
|
|
909
|
+
color: TEXT_SECONDARY
|
|
910
|
+
},
|
|
911
|
+
testID: "meso-status-card-subtitle",
|
|
912
|
+
children: mesoSubtitle
|
|
913
|
+
}
|
|
914
|
+
)
|
|
915
|
+
] }),
|
|
916
|
+
metrics.length > 0 && /* @__PURE__ */ jsx(
|
|
917
|
+
View,
|
|
918
|
+
{
|
|
919
|
+
className: "flex-row flex-wrap",
|
|
920
|
+
style: { gap: 8 },
|
|
921
|
+
testID: "meso-status-card-metrics",
|
|
922
|
+
children: metrics.map((metric, index) => /* @__PURE__ */ jsx(MetricCell, { metric }, `${metric.label}-${index}`))
|
|
923
|
+
}
|
|
924
|
+
),
|
|
925
|
+
gauges.length > 0 && /* @__PURE__ */ jsx(View, { style: { gap: 12 }, testID: "meso-status-card-gauges", children: gauges.map((gauge, index) => /* @__PURE__ */ jsx(Gauge, { gauge }, `${gauge.label}-${index}`)) }),
|
|
926
|
+
coaching != null && /* @__PURE__ */ jsx(
|
|
927
|
+
View,
|
|
928
|
+
{
|
|
929
|
+
style: {
|
|
930
|
+
backgroundColor: "rgba(255,176,32,0.06)",
|
|
931
|
+
borderWidth: 1,
|
|
932
|
+
borderColor: "rgba(255,176,32,0.15)",
|
|
933
|
+
borderRadius: 8,
|
|
934
|
+
paddingVertical: 10,
|
|
935
|
+
paddingHorizontal: 12
|
|
936
|
+
},
|
|
937
|
+
testID: "meso-status-card-coaching",
|
|
938
|
+
children: /* @__PURE__ */ jsx(
|
|
939
|
+
Text,
|
|
940
|
+
{
|
|
941
|
+
style: {
|
|
942
|
+
fontSize: 12,
|
|
943
|
+
lineHeight: 17,
|
|
944
|
+
fontFamily: "Inter, sans-serif",
|
|
945
|
+
color: TEXT_SECONDARY
|
|
946
|
+
},
|
|
947
|
+
children: renderCoachingText(coaching.text, coaching.highlights)
|
|
948
|
+
}
|
|
949
|
+
)
|
|
950
|
+
}
|
|
951
|
+
),
|
|
952
|
+
nextTarget != null && /* @__PURE__ */ jsxs(
|
|
953
|
+
View,
|
|
954
|
+
{
|
|
955
|
+
className: "flex-row items-center",
|
|
956
|
+
style: {
|
|
957
|
+
gap: 8,
|
|
958
|
+
backgroundColor: "rgba(20,184,166,0.06)",
|
|
959
|
+
borderWidth: 1,
|
|
960
|
+
borderColor: "rgba(20,184,166,0.2)",
|
|
961
|
+
borderRadius: 8,
|
|
962
|
+
paddingVertical: 10,
|
|
963
|
+
paddingHorizontal: 12
|
|
964
|
+
},
|
|
965
|
+
testID: "meso-status-card-next-target",
|
|
966
|
+
children: [
|
|
967
|
+
/* @__PURE__ */ jsx(Text, { style: { fontSize: 14, color: SUCCESS }, accessibilityElementsHidden: true, children: nextTarget.icon }),
|
|
968
|
+
/* @__PURE__ */ jsx(
|
|
969
|
+
Text,
|
|
970
|
+
{
|
|
971
|
+
style: {
|
|
972
|
+
flexShrink: 1,
|
|
973
|
+
fontSize: 12,
|
|
974
|
+
fontFamily: "Inter, sans-serif",
|
|
975
|
+
fontWeight: "600",
|
|
976
|
+
color: SUCCESS
|
|
977
|
+
},
|
|
978
|
+
children: nextTarget.text
|
|
979
|
+
}
|
|
980
|
+
)
|
|
981
|
+
]
|
|
982
|
+
}
|
|
983
|
+
)
|
|
984
|
+
] })
|
|
985
|
+
]
|
|
986
|
+
}
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// src/theme/workout-tokens.ts
|
|
991
|
+
var WORKOUT_TOKENS = {
|
|
992
|
+
// Canonical 4-band performance scale — the single source for BOTH the
|
|
993
|
+
// VelocityStrip zone bars and the SetRow RPE color (TD-03.43). The direction
|
|
994
|
+
// is intentionally inverted between the two consumers: for velocity, green =
|
|
995
|
+
// fastest/best; for RPE, green = easiest. Each component maps its own
|
|
996
|
+
// thresholds onto these four colors. Kept as vivid data-viz values (distinct
|
|
997
|
+
// from the muted status tokens) and theme-independent, so — like the heatmap
|
|
998
|
+
// scale below — they are consumed inline as a JS import rather than CSS vars.
|
|
999
|
+
// A JS import also resolves on native RN, unlike var(--…) strings.
|
|
1000
|
+
scale: {
|
|
1001
|
+
green: "#2ed573",
|
|
1002
|
+
yellow: "#ffd43b",
|
|
1003
|
+
orange: "#ffa502",
|
|
1004
|
+
red: "#ff4757"
|
|
1005
|
+
},
|
|
1006
|
+
// BodyMap volume heatmap scale (drive dynamic SVG fills, so inline values).
|
|
1007
|
+
// Maps weekly-volume status against MEV/MAV/MRV landmarks to a fill color.
|
|
1008
|
+
heatmap: {
|
|
1009
|
+
none: "#E0E0E0",
|
|
1010
|
+
// gray — no training data
|
|
1011
|
+
under: "#4A90D9",
|
|
1012
|
+
// cool blue — below MEV
|
|
1013
|
+
maintenance: "#F5C842",
|
|
1014
|
+
// warm yellow — MEV to MAV
|
|
1015
|
+
productive: "#4CAF50",
|
|
1016
|
+
// green — MAV to MRV
|
|
1017
|
+
approaching: "#FFA502",
|
|
1018
|
+
// orange — near MRV
|
|
1019
|
+
over: "#FF6B35"
|
|
1020
|
+
// red-orange — over MRV
|
|
1021
|
+
},
|
|
1022
|
+
// Badge border-radius (rounded-sm is 4px, we need 2px)
|
|
1023
|
+
badgeRadius: 2,
|
|
1024
|
+
// Surface and border colors were removed here: those flip with the theme, so
|
|
1025
|
+
// components use the CSS custom properties (var(--color-surface-*/border-*))
|
|
1026
|
+
// directly. Only theme-independent data-viz values remain below.
|
|
1027
|
+
// Intensity bar specific
|
|
1028
|
+
intensity: {
|
|
1029
|
+
track: "#333333",
|
|
1030
|
+
over1: "#D14343",
|
|
1031
|
+
over2: "#A62626",
|
|
1032
|
+
over3: "#7A1C1C",
|
|
1033
|
+
targetLine: "rgba(33, 150, 243, 0.5)",
|
|
1034
|
+
atTargetGlow: "0 0 5px 1px rgba(33, 150, 243, 0.35), 0 0 10px 3px rgba(33, 150, 243, 0.15)"
|
|
1035
|
+
},
|
|
1036
|
+
// Placeholder strip
|
|
1037
|
+
placeholder: {
|
|
1038
|
+
fill: "#3A3A3A"
|
|
1039
|
+
},
|
|
1040
|
+
// Deviation bar
|
|
1041
|
+
deviation: {
|
|
1042
|
+
track: "#333333"
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
|
|
1046
|
+
export { Badge, BadgeText, Card, CardContent, CardDescription, CardFooter, CardHeader, CardSkeleton, CardTitle, Indicator, MesoStatusCard, Sparkline, StatusDot, WORKOUT_TOKENS, cn, jsx, jsxs };
|
|
1047
|
+
//# sourceMappingURL=chunk-TOD2WQBG.mjs.map
|
|
1048
|
+
//# sourceMappingURL=chunk-TOD2WQBG.mjs.map
|