@titan-design/react-ui 0.4.0 → 0.5.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 (67) hide show
  1. package/dist/MesoStatusCard-CQk71hSi.d.mts +77 -0
  2. package/dist/MesoStatusCard-CQk71hSi.d.ts +77 -0
  3. package/dist/bodymap.d.mts +246 -3
  4. package/dist/bodymap.d.ts +246 -3
  5. package/dist/bodymap.mjs +1911 -6
  6. package/dist/bodymap.mjs.map +1 -1
  7. package/dist/index.d.mts +31 -738
  8. package/dist/index.d.ts +31 -738
  9. package/dist/index.js +894 -1692
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +10287 -8203
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/pages-D7Jlssvp.d.ts +791 -0
  14. package/dist/pages-MO0JCySL.d.mts +791 -0
  15. package/dist/pages.d.mts +4 -0
  16. package/dist/pages.d.ts +4 -0
  17. package/dist/pages.js +6646 -0
  18. package/dist/pages.js.map +1 -0
  19. package/dist/pages.mjs +6626 -0
  20. package/dist/pages.mjs.map +1 -0
  21. package/dist/theme/index.d.mts +12 -12
  22. package/dist/theme/index.d.ts +12 -12
  23. package/dist/theme/index.js +30 -30
  24. package/dist/theme/index.js.map +1 -1
  25. package/dist/theme/index.mjs +1880 -2
  26. package/dist/theme/index.mjs.map +1 -1
  27. package/dist/theme/tokens-css.mjs +573 -1
  28. package/dist/theme/tokens-css.mjs.map +1 -1
  29. package/package.json +9 -7
  30. package/src/components/custom/Workout/BaseBadge.stories.tsx +3 -3
  31. package/src/components/custom/Workout/BaseBadge.test.tsx +1 -1
  32. package/src/components/custom/Workout/BaseBadge.tsx +1 -1
  33. package/src/components/custom/Workout/ExerciseCard.tsx +5 -1
  34. package/src/components/custom/Workout/ExerciseDetailPage.tsx +17 -4
  35. package/src/components/custom/Workout/IntensityBar.test.tsx +112 -60
  36. package/src/components/custom/Workout/IntensityBar.tsx +109 -58
  37. package/src/components/custom/Workout/PlaceholderStrip.tsx +5 -9
  38. package/src/components/custom/Workout/PrBadge.tsx +3 -3
  39. package/src/components/custom/Workout/PrHistoryModal.tsx +2 -2
  40. package/src/components/custom/Workout/README.md +50 -0
  41. package/src/components/custom/Workout/RestTimer.test.tsx +41 -10
  42. package/src/components/custom/Workout/RestTimer.tsx +48 -43
  43. package/src/components/custom/Workout/SetRow.tsx +10 -1
  44. package/src/components/custom/Workout/TempoDisplay.stories.tsx +69 -2
  45. package/src/components/custom/Workout/TempoDisplay.test.tsx +35 -0
  46. package/src/components/custom/Workout/TempoDisplay.tsx +104 -1
  47. package/src/components/custom/Workout/VelocityStrip.test.tsx +89 -2
  48. package/src/components/custom/Workout/VelocityStrip.tsx +203 -38
  49. package/src/components/custom/Workout/WeekRow.test.tsx +2 -2
  50. package/src/components/custom/Workout/WeightBadge.test.tsx +2 -2
  51. package/src/components/custom/Workout/WeightBadge.tsx +2 -2
  52. package/src/components/custom/Workout/icons.tsx +68 -0
  53. package/src/components/custom/Workout/index.ts +38 -47
  54. package/src/index.ts +3 -2
  55. package/src/pages.ts +61 -0
  56. package/src/test/stories-smoke.test.tsx +50 -0
  57. package/src/theme/barrel.ts +53 -0
  58. package/src/theme/index.ts +8 -43
  59. package/src/theme/native-theming-guard.test.ts +117 -0
  60. package/dist/bodymap-BhG55xHM.d.mts +0 -318
  61. package/dist/bodymap-BhG55xHM.d.ts +0 -318
  62. package/dist/chunk-2SISKTWM.mjs +0 -1027
  63. package/dist/chunk-2SISKTWM.mjs.map +0 -1
  64. package/dist/chunk-7XPB4NAO.mjs +0 -1013
  65. package/dist/chunk-7XPB4NAO.mjs.map +0 -1
  66. package/dist/chunk-SNVKL5WZ.mjs +0 -883
  67. package/dist/chunk-SNVKL5WZ.mjs.map +0 -1
package/dist/bodymap.mjs CHANGED
@@ -1,9 +1,409 @@
1
- import { WORKOUT_TOKENS, cn, Badge, Sparkline, MesoStatusCard } from './chunk-2SISKTWM.mjs';
2
- import { jsxs, jsx, resolveColor } from './chunk-SNVKL5WZ.mjs';
3
- import './chunk-7XPB4NAO.mjs';
4
- import { useMemo, useState, useEffect } from 'react';
5
- import { Animated, Easing, View, Text, Pressable, ScrollView } from 'react-native';
1
+ import { useMemo, useState, useEffect, Fragment as Fragment$1, useSyncExternalStore } from 'react';
2
+ import { Platform, Animated, Easing, View, Text, Pressable, ScrollView } from 'react-native';
6
3
  import BodyHighlighter from 'react-native-body-highlighter';
4
+ import { clsx } from 'clsx';
5
+ import { twMerge } from 'tailwind-merge';
6
+ import { Fragment, jsx as jsx$1, jsxs as jsxs$1 } from 'react/jsx-runtime';
7
+
8
+ // src/components/custom/Workout/BodyMap.tsx
9
+ function cn(...inputs) {
10
+ return twMerge(clsx(inputs));
11
+ }
12
+
13
+ // src/theme/tokens/primitives.ts
14
+ var primitiveColors = {
15
+ // Blue scale (primary brand)
16
+ blue: {
17
+ 400: "#60A5FA",
18
+ 600: "#5048E5",
19
+ // Primary main
20
+ 700: "#3832A0"},
21
+ // Teal scale (success)
22
+ teal: {
23
+ 50: "#F0FDFA",
24
+ 400: "#43C6B7",
25
+ // Success light
26
+ 500: "#14B8A6",
27
+ 700: "#0E8074"},
28
+ // Red scale (error)
29
+ red: {
30
+ 50: "#FEF2F2",
31
+ 400: "#DA6868",
32
+ // Error light
33
+ 500: "#EF4444",
34
+ 600: "#D14343",
35
+ // Error main
36
+ 700: "#922E2E"},
37
+ // Amber scale (warning)
38
+ amber: {
39
+ 50: "#FFFBEB",
40
+ 400: "#FFBF4C",
41
+ // Warning light
42
+ 500: "#FFB020",
43
+ // Warning main
44
+ 600: "#B27B16"},
45
+ // Sky/Blue scale (info)
46
+ sky: {
47
+ 50: "#F0F9FF",
48
+ 400: "#64B6F7",
49
+ // Info light
50
+ 500: "#2196F3",
51
+ 700: "#0B79D0"},
52
+ // Neutral/Gray scale
53
+ neutral: {
54
+ 50: "#FAFAFA",
55
+ 100: "#F3F4F6",
56
+ 300: "#D1D5DB",
57
+ 400: "#9CA3AF",
58
+ 500: "#6B7280",
59
+ 600: "#4B5563",
60
+ 900: "#111827"},
61
+ // Orange scale (accent/brand color)
62
+ accent: {
63
+ 200: "#FF9630",
64
+ 300: "#FF8500",
65
+ 400: "#FF7900",
66
+ 500: "#F56C00",
67
+ 600: "#E06D10",
68
+ 700: "#D0620C"},
69
+ // Charcoal scale (dark backgrounds)
70
+ charcoal: {
71
+ 300: "#2C2C2C",
72
+ 400: "#1F1F1F",
73
+ 500: "#1C1C1C",
74
+ 600: "#191919",
75
+ 700: "#161616",
76
+ 900: "#101010"
77
+ },
78
+ // Steel scale (cool accent)
79
+ steel: {
80
+ 300: "#678498",
81
+ 400: "#557488",
82
+ 500: "#406D87",
83
+ 600: "#39617A",
84
+ 700: "#32556D"},
85
+ // Pure colors
86
+ white: "#FFFFFF"};
87
+ var discreteRainbow = [
88
+ "#E8ECFB",
89
+ "#D9CCE3",
90
+ "#D1BBD7",
91
+ "#CAACCB",
92
+ "#BA8DB4",
93
+ "#AE76A3",
94
+ "#AA6F9E",
95
+ "#994F88",
96
+ "#882E72",
97
+ "#1965B0",
98
+ "#437DBF",
99
+ "#5289C7",
100
+ "#6195CF",
101
+ "#7BAFDE",
102
+ "#4EB265",
103
+ "#90C987",
104
+ "#CAE0AB",
105
+ "#F7F056",
106
+ "#F7CB45",
107
+ "#F6C141",
108
+ "#F4A736",
109
+ "#F1932D",
110
+ "#EE8026",
111
+ "#E8601C",
112
+ "#E65518",
113
+ "#DC050C",
114
+ "#A5170E",
115
+ "#72190E",
116
+ "#42150A"
117
+ ];
118
+
119
+ // src/theme/tokens/semantic.ts
120
+ var semanticColorsLight = {
121
+ // Brand colors (brand-*)
122
+ "brand-primary": primitiveColors.accent[400],
123
+ // #FF7900
124
+ "brand-primary-light": primitiveColors.accent[200],
125
+ // #FF9630
126
+ "brand-primary-dark": primitiveColors.accent[700],
127
+ // #D0620C
128
+ "brand-primary-subtle": "rgba(255, 121, 0, 0.08)",
129
+ "brand-primary-hover": primitiveColors.accent[500],
130
+ "brand-primary-active": primitiveColors.accent[600],
131
+ "brand-secondary": primitiveColors.steel[500],
132
+ // #406D87
133
+ "brand-secondary-light": primitiveColors.steel[300],
134
+ // #678498
135
+ "brand-secondary-dark": primitiveColors.steel[700],
136
+ // #32556D
137
+ "brand-secondary-subtle": "rgba(64, 109, 135, 0.08)",
138
+ "brand-secondary-hover": primitiveColors.steel[600],
139
+ "brand-secondary-active": primitiveColors.steel[700],
140
+ // Text on brand backgrounds (on-*)
141
+ "on-brand-primary": primitiveColors.white,
142
+ "on-brand-secondary": primitiveColors.white,
143
+ // Status colors (status-*)
144
+ "status-success": primitiveColors.teal[500],
145
+ // #14B8A6
146
+ "status-success-light": primitiveColors.teal[400],
147
+ // #43C6B7
148
+ "status-success-dark": primitiveColors.teal[700],
149
+ // #0E8074
150
+ "status-success-subtle": primitiveColors.teal[50],
151
+ "status-error": primitiveColors.red[600],
152
+ // #D14343
153
+ "status-error-light": primitiveColors.red[400],
154
+ // #DA6868
155
+ "status-error-dark": primitiveColors.red[700],
156
+ // #922E2E
157
+ "status-error-subtle": primitiveColors.red[50],
158
+ "status-warning": primitiveColors.amber[500],
159
+ // #FFB020
160
+ "status-warning-light": primitiveColors.amber[400],
161
+ // #FFBF4C
162
+ "status-warning-dark": primitiveColors.amber[600],
163
+ // #B27B16
164
+ "status-warning-subtle": primitiveColors.amber[50],
165
+ "status-info": primitiveColors.sky[500],
166
+ // #2196F3
167
+ "status-info-light": primitiveColors.sky[400],
168
+ // #64B6F7
169
+ "status-info-dark": primitiveColors.sky[700],
170
+ // #0B79D0
171
+ "status-info-subtle": primitiveColors.sky[50],
172
+ // Text on status backgrounds (on-status-*)
173
+ "on-status-success": primitiveColors.white,
174
+ "on-status-error": primitiveColors.white,
175
+ "on-status-warning": primitiveColors.white,
176
+ "on-status-info": primitiveColors.white,
177
+ // Result/outcome indicators (result-*)
178
+ "result-improve": "#4caf50",
179
+ // Green - positive outcome
180
+ "result-improve-light": "rgba(76, 175, 80, 0.12)",
181
+ "result-improve-dark": "#248a24",
182
+ "result-degrade": "#ef5350",
183
+ // Red - negative outcome
184
+ "result-degrade-light": "rgba(239, 83, 80, 0.12)",
185
+ "result-degrade-dark": "#b30000",
186
+ "result-inconclusive": "#9E9A97",
187
+ // Gray - no clear result
188
+ "result-inconclusive-light": "rgba(158, 154, 151, 0.12)",
189
+ "result-neutral": primitiveColors.neutral[500],
190
+ // Neutral baseline
191
+ // Text on result backgrounds (on-result-*)
192
+ "on-result-improve": primitiveColors.white,
193
+ "on-result-degrade": primitiveColors.white,
194
+ "on-result-inconclusive": primitiveColors.white,
195
+ // Data visualization colors (data-*)
196
+ // First 10 colors from discrete rainbow optimized for charts
197
+ "data-1": discreteRainbow[9],
198
+ // #1965B0 - Blue
199
+ "data-2": discreteRainbow[14],
200
+ // #4EB265 - Green
201
+ "data-3": discreteRainbow[17],
202
+ // #F7F056 - Yellow
203
+ "data-4": discreteRainbow[25],
204
+ // #DC050C - Red
205
+ "data-5": discreteRainbow[8],
206
+ // #882E72 - Purple
207
+ "data-6": discreteRainbow[20],
208
+ // #F4A736 - Orange
209
+ "data-7": discreteRainbow[13],
210
+ // #7BAFDE - Light Blue
211
+ "data-8": discreteRainbow[15],
212
+ // #90C987 - Light Green
213
+ "data-9": discreteRainbow[3],
214
+ // #CAACCB - Lavender
215
+ "data-10": discreteRainbow[22],
216
+ // #EE8026 - Dark Orange
217
+ // Text colors (text-*)
218
+ "text-primary": "#121828",
219
+ "text-secondary": "#65748B",
220
+ "text-tertiary": primitiveColors.neutral[400],
221
+ "text-disabled": "rgba(55, 65, 81, 0.48)",
222
+ "text-inverse": primitiveColors.white,
223
+ "text-link": primitiveColors.blue[600],
224
+ "text-link-hover": primitiveColors.blue[700],
225
+ // Surface colors (surface-*) - for elevated containers
226
+ "surface-base": primitiveColors.white,
227
+ "surface-elevated": primitiveColors.neutral[50],
228
+ // #FAFAFA - slightly off-white for elevated cards
229
+ "surface-raised": primitiveColors.neutral[100],
230
+ // #F3F4F6 - light gray for raised cards
231
+ "surface-overlay": primitiveColors.white,
232
+ "surface-input": primitiveColors.neutral[50],
233
+ // Input field background (filled variant)
234
+ // Background colors (background-*)
235
+ "background-base": "#EBEBEB",
236
+ "background-default": primitiveColors.white,
237
+ "background-subtle": primitiveColors.neutral[50],
238
+ // Border colors (border-*)
239
+ "border-default": "#E8E9EB",
240
+ "border-subtle": primitiveColors.neutral[100],
241
+ "border-strong": primitiveColors.neutral[300],
242
+ "border-focus": primitiveColors.blue[600],
243
+ "border-input": primitiveColors.neutral[300],
244
+ // Input field border
245
+ "border-input-hover": primitiveColors.neutral[400],
246
+ // Input field border on hover
247
+ "border-input-focus": primitiveColors.blue[600],
248
+ // Input field border on focus
249
+ "border-input-error": primitiveColors.red[600],
250
+ // Input field border on error
251
+ // Interactive states (interactive-*)
252
+ "interactive-hover": "rgba(55, 65, 81, 0.04)",
253
+ "interactive-focus": "rgba(55, 65, 81, 0.12)",
254
+ "interactive-active": "rgba(55, 65, 81, 0.16)",
255
+ "interactive-selected": "rgba(55, 65, 81, 0.08)",
256
+ "interactive-disabled": "rgba(55, 65, 81, 0.12)",
257
+ "interactive-disabled-text": "rgba(55, 65, 81, 0.26)",
258
+ // Divider
259
+ "divider": "#E8E9EB",
260
+ // Avatar default
261
+ "avatar-background": primitiveColors.neutral[500],
262
+ "avatar-text": primitiveColors.white
263
+ };
264
+ var semanticColorsDark = {
265
+ // Brand colors stay the same in dark mode
266
+ "brand-primary": primitiveColors.accent[400],
267
+ "brand-primary-light": primitiveColors.accent[200],
268
+ "brand-primary-dark": primitiveColors.accent[700],
269
+ "brand-primary-subtle": "rgba(255, 121, 0, 0.12)",
270
+ "brand-primary-hover": primitiveColors.accent[300],
271
+ "brand-primary-active": primitiveColors.accent[200],
272
+ "brand-secondary": primitiveColors.steel[500],
273
+ "brand-secondary-light": primitiveColors.steel[300],
274
+ "brand-secondary-dark": primitiveColors.steel[700],
275
+ "brand-secondary-subtle": "rgba(64, 109, 135, 0.12)",
276
+ "brand-secondary-hover": primitiveColors.steel[400],
277
+ "brand-secondary-active": primitiveColors.steel[300],
278
+ // Text on brand backgrounds
279
+ "on-brand-primary": primitiveColors.white,
280
+ "on-brand-secondary": primitiveColors.white,
281
+ // Status colors
282
+ "status-success": primitiveColors.teal[500],
283
+ "status-success-light": primitiveColors.teal[400],
284
+ "status-success-dark": primitiveColors.teal[700],
285
+ "status-success-subtle": "rgba(20, 184, 166, 0.12)",
286
+ "status-error": primitiveColors.red[600],
287
+ "status-error-light": primitiveColors.red[400],
288
+ "status-error-dark": primitiveColors.red[700],
289
+ "status-error-subtle": "rgba(209, 67, 67, 0.12)",
290
+ "status-warning": primitiveColors.amber[500],
291
+ "status-warning-light": primitiveColors.amber[400],
292
+ "status-warning-dark": primitiveColors.amber[600],
293
+ "status-warning-subtle": "rgba(255, 176, 32, 0.12)",
294
+ "status-info": primitiveColors.sky[500],
295
+ "status-info-light": primitiveColors.sky[400],
296
+ "status-info-dark": primitiveColors.sky[700],
297
+ "status-info-subtle": "rgba(33, 150, 243, 0.12)",
298
+ // Text on status backgrounds
299
+ "on-status-success": primitiveColors.white,
300
+ "on-status-error": primitiveColors.white,
301
+ "on-status-warning": primitiveColors.white,
302
+ "on-status-info": primitiveColors.white,
303
+ // Result/outcome indicators (result-*)
304
+ "result-improve": "#4caf50",
305
+ "result-improve-light": "rgba(76, 175, 80, 0.16)",
306
+ "result-improve-dark": "#248a24",
307
+ "result-degrade": "#ef5350",
308
+ "result-degrade-light": "rgba(239, 83, 80, 0.16)",
309
+ "result-degrade-dark": "#b30000",
310
+ "result-inconclusive": "#9E9A97",
311
+ "result-inconclusive-light": "rgba(158, 154, 151, 0.16)",
312
+ "result-neutral": primitiveColors.neutral[400],
313
+ // Text on result backgrounds
314
+ "on-result-improve": primitiveColors.white,
315
+ "on-result-degrade": primitiveColors.white,
316
+ "on-result-inconclusive": primitiveColors.white,
317
+ // Data visualization colors (same in dark mode for consistency)
318
+ "data-1": discreteRainbow[9],
319
+ "data-2": discreteRainbow[14],
320
+ "data-3": discreteRainbow[17],
321
+ "data-4": discreteRainbow[25],
322
+ "data-5": discreteRainbow[8],
323
+ "data-6": discreteRainbow[20],
324
+ "data-7": discreteRainbow[13],
325
+ "data-8": discreteRainbow[15],
326
+ "data-9": discreteRainbow[3],
327
+ "data-10": discreteRainbow[22],
328
+ // Text colors - inverted for dark mode
329
+ "text-primary": primitiveColors.neutral[100],
330
+ "text-secondary": primitiveColors.neutral[400],
331
+ "text-tertiary": primitiveColors.neutral[500],
332
+ "text-disabled": "rgba(255, 255, 255, 0.38)",
333
+ "text-inverse": primitiveColors.neutral[900],
334
+ "text-link": "#828DF8",
335
+ "text-link-hover": primitiveColors.blue[400],
336
+ // Surface colors - dark backgrounds
337
+ "surface-base": primitiveColors.charcoal[700],
338
+ // #161616 - main surface
339
+ "surface-elevated": primitiveColors.charcoal[600],
340
+ // #191919 - elevated surface
341
+ "surface-raised": primitiveColors.charcoal[500],
342
+ // #1C1C1C - raised surface
343
+ "surface-overlay": primitiveColors.charcoal[600],
344
+ // #191919 - overlay surface
345
+ "surface-input": primitiveColors.charcoal[600],
346
+ // #191919 - input surface
347
+ // Background colors
348
+ "background-base": primitiveColors.charcoal[900],
349
+ // #101010 - darkest charcoal
350
+ "background-default": primitiveColors.charcoal[700],
351
+ // #161616 - main background
352
+ "background-subtle": primitiveColors.charcoal[500],
353
+ // #1C1C1C - subtle background
354
+ // Border colors
355
+ "border-default": primitiveColors.charcoal[400],
356
+ // #1F1F1F - default border
357
+ "border-subtle": primitiveColors.charcoal[500],
358
+ // #1C1C1C - subtle border
359
+ "border-strong": primitiveColors.charcoal[300],
360
+ // #2C2C2C - strong border
361
+ "border-focus": "#828DF8",
362
+ "border-input": primitiveColors.neutral[600],
363
+ "border-input-hover": primitiveColors.neutral[500],
364
+ "border-input-focus": "#828DF8",
365
+ "border-input-error": primitiveColors.red[500],
366
+ // Interactive states
367
+ "interactive-hover": "rgba(255, 255, 255, 0.04)",
368
+ "interactive-focus": "rgba(255, 255, 255, 0.12)",
369
+ "interactive-active": "rgba(255, 255, 255, 0.16)",
370
+ "interactive-selected": "rgba(255, 255, 255, 0.08)",
371
+ "interactive-disabled": "rgba(255, 255, 255, 0.12)",
372
+ "interactive-disabled-text": "rgba(255, 255, 255, 0.26)",
373
+ // Divider
374
+ "divider": primitiveColors.charcoal[400],
375
+ // #1F1F1F - divider color
376
+ // Avatar default
377
+ "avatar-background": primitiveColors.neutral[600],
378
+ "avatar-text": primitiveColors.white
379
+ };
380
+ function getSemanticColors(mode) {
381
+ return mode === "dark" ? semanticColorsDark : semanticColorsLight;
382
+ }
383
+
384
+ // src/theme/resolve-color.ts
385
+ function resolveColor(token, mode = "dark") {
386
+ return Platform.OS === "web" ? `var(--color-${token})` : getSemanticColors(mode)[token];
387
+ }
388
+
389
+ // src/theme/workout-tokens.ts
390
+ var WORKOUT_TOKENS = {
391
+ // BodyMap volume heatmap scale (drive dynamic SVG fills, so inline values).
392
+ // Maps weekly-volume status against MEV/MAV/MRV landmarks to a fill color.
393
+ heatmap: {
394
+ none: "#E0E0E0",
395
+ // gray — no training data
396
+ under: "#4A90D9",
397
+ // cool blue — below MEV
398
+ maintenance: "#F5C842",
399
+ // warm yellow — MEV to MAV
400
+ productive: "#4CAF50",
401
+ // green — MAV to MRV
402
+ approaching: "#FFA502",
403
+ // orange — near MRV
404
+ over: "#FF6B35"
405
+ // red-orange — over MRV
406
+ }};
7
407
 
8
408
  // src/components/custom/Workout/muscleTaxonomy.ts
9
409
  var MuscleGroup = /* @__PURE__ */ ((MuscleGroup5) => {
@@ -188,6 +588,23 @@ var SLUG_TO_PRIMARY_MUSCLE = {
188
588
  abs: "abs" /* ABS */,
189
589
  obliques: "obliques" /* OBLIQUES */
190
590
  };
591
+ function wrapJsx(fn) {
592
+ return function(type, props, ...rest) {
593
+ if (props && typeof props.className === "string" && props.className) {
594
+ const cn2 = props.className;
595
+ const cssStyle = { $$css: true, [cn2]: cn2 };
596
+ const existing = props.style;
597
+ props = {
598
+ ...props,
599
+ style: existing ? [cssStyle, existing] : cssStyle
600
+ };
601
+ delete props.className;
602
+ }
603
+ return fn(type, props, ...rest);
604
+ };
605
+ }
606
+ var jsx = wrapJsx(jsx$1);
607
+ var jsxs = wrapJsx(jsxs$1);
191
608
 
192
609
  // src/components/custom/Workout/BodyMap.tsx
193
610
  var Body = BodyHighlighter.default ?? BodyHighlighter;
@@ -440,7 +857,279 @@ function MuscleButton({ entry, highlighted, onMusclePress }) {
440
857
  }
441
858
  );
442
859
  }
860
+ var sizeStyles = {
861
+ xs: "w-1 h-1",
862
+ sm: "w-1.5 h-1.5",
863
+ md: "w-2 h-2",
864
+ lg: "w-2.5 h-2.5"
865
+ };
866
+ var colorStyles = {
867
+ default: "bg-text-tertiary",
868
+ primary: "bg-brand-primary",
869
+ success: "bg-status-success",
870
+ error: "bg-status-error",
871
+ warning: "bg-status-warning",
872
+ info: "bg-status-info"
873
+ };
874
+ function Indicator({
875
+ size = "sm",
876
+ color = "default",
877
+ customColor,
878
+ glow = false,
879
+ ring = false,
880
+ className,
881
+ style,
882
+ ...props
883
+ }) {
884
+ const dynamicStyle = {
885
+ ...typeof style === "object" ? style : {},
886
+ ...customColor ? { backgroundColor: customColor } : {},
887
+ ...glow && !customColor ? {} : {},
888
+ ...glow && customColor ? { boxShadow: `0 0 6px ${customColor}` } : {}
889
+ };
890
+ return /* @__PURE__ */ jsx(
891
+ View,
892
+ {
893
+ className: cn(
894
+ "rounded-full shrink-0",
895
+ sizeStyles[size],
896
+ !customColor && colorStyles[color],
897
+ ring && "border-2 border-background-base",
898
+ glow && !customColor && color === "success" && "shadow-[0_0_6px_rgba(20,184,166,0.6)]",
899
+ glow && !customColor && color === "error" && "shadow-[0_0_6px_rgba(239,68,68,0.6)]",
900
+ glow && !customColor && color === "warning" && "shadow-[0_0_6px_rgba(245,158,11,0.6)]",
901
+ glow && !customColor && color === "primary" && "shadow-[0_0_6px_rgba(255,121,0,0.6)]",
902
+ className
903
+ ),
904
+ style: Object.keys(dynamicStyle).length > 0 ? dynamicStyle : style,
905
+ ...props
906
+ }
907
+ );
908
+ }
909
+
910
+ // src/components/ui/badge/Badge.tsx
911
+ var variantColorStyles = {
912
+ solid: {
913
+ default: "bg-border-strong text-text-inverse",
914
+ primary: "bg-brand-primary text-text-inverse",
915
+ secondary: "bg-brand-secondary text-text-inverse",
916
+ success: "bg-status-success text-text-inverse",
917
+ error: "bg-status-error text-text-inverse",
918
+ warning: "bg-status-warning text-text-inverse",
919
+ info: "bg-status-info text-text-inverse"
920
+ },
921
+ subtle: {
922
+ default: "bg-surface-raised text-text-secondary",
923
+ primary: "bg-brand-primary-subtle text-brand-primary",
924
+ secondary: "bg-brand-secondary-subtle text-brand-secondary",
925
+ success: "bg-status-success-subtle text-status-success",
926
+ error: "bg-status-error-subtle text-status-error",
927
+ warning: "bg-status-warning-subtle text-status-warning",
928
+ info: "bg-status-info-subtle text-status-info"
929
+ },
930
+ outline: {
931
+ default: "border border-border-strong text-text-secondary",
932
+ primary: "border border-brand-primary text-brand-primary",
933
+ secondary: "border border-brand-secondary text-brand-secondary",
934
+ success: "border border-status-success text-status-success",
935
+ error: "border border-status-error text-status-error",
936
+ warning: "border border-status-warning text-status-warning",
937
+ info: "border border-status-info text-status-info"
938
+ }
939
+ };
940
+ var sizeStyles2 = {
941
+ sm: "px-1.5 py-0.5 text-xs",
942
+ md: "px-2 py-0.5 text-xs",
943
+ lg: "px-2.5 py-1 text-sm"
944
+ };
945
+ function Badge({
946
+ variant = "subtle",
947
+ color = "default",
948
+ size = "md",
949
+ dot,
950
+ dotColor,
951
+ className,
952
+ children,
953
+ ...props
954
+ }) {
955
+ return /* @__PURE__ */ jsxs(
956
+ View,
957
+ {
958
+ className: cn(
959
+ "inline-flex items-center justify-center rounded-full font-medium",
960
+ variantColorStyles[variant][color],
961
+ sizeStyles2[size],
962
+ className
963
+ ),
964
+ ...props,
965
+ children: [
966
+ dot && /* @__PURE__ */ jsx(
967
+ Indicator,
968
+ {
969
+ size: "xs",
970
+ color: dotColor ?? (color === "default" || color === "secondary" ? "default" : color),
971
+ className: "mr-0.5"
972
+ }
973
+ ),
974
+ typeof children === "string" ? /* @__PURE__ */ jsx(Text, { className: "text-inherit font-medium", children }) : children
975
+ ]
976
+ }
977
+ );
978
+ }
443
979
  var BRAND_PRIMARY = "#FF7900";
980
+ function normalizeData(data, height) {
981
+ if (data.length === 0) return [];
982
+ const min = Math.min(...data);
983
+ const max = Math.max(...data);
984
+ const range = max - min || 1;
985
+ return data.map((v) => height - (v - min) / range * height);
986
+ }
987
+ function normalizeValue(value, data, height) {
988
+ const min = Math.min(...data);
989
+ const max = Math.max(...data);
990
+ const range = max - min || 1;
991
+ return height - (value - min) / range * height;
992
+ }
993
+ function Sparkline({
994
+ data,
995
+ width = 80,
996
+ height = 30,
997
+ color,
998
+ showDots = false,
999
+ referenceLines,
1000
+ highlightLast = false,
1001
+ className,
1002
+ ...props
1003
+ }) {
1004
+ if (data.length === 0) {
1005
+ return /* @__PURE__ */ jsx(
1006
+ View,
1007
+ {
1008
+ style: { width, height },
1009
+ className,
1010
+ testID: "sparkline-empty",
1011
+ accessibilityLabel: "Sparkline chart, no data",
1012
+ ...props
1013
+ }
1014
+ );
1015
+ }
1016
+ const resolvedColor = color ?? BRAND_PRIMARY;
1017
+ const normalized = normalizeData(data, height);
1018
+ const stepX = data.length > 1 ? width / (data.length - 1) : 0;
1019
+ const dotSize = 3;
1020
+ const highlightSize = 6;
1021
+ return /* @__PURE__ */ jsxs(
1022
+ View,
1023
+ {
1024
+ style: { width, height, position: "relative" },
1025
+ className: cn(className),
1026
+ accessibilityRole: "image",
1027
+ accessibilityLabel: `Sparkline chart with ${data.length} data points`,
1028
+ testID: "sparkline",
1029
+ ...props,
1030
+ children: [
1031
+ referenceLines?.map((line, i) => {
1032
+ const y = normalizeValue(line.value, data, height);
1033
+ return /* @__PURE__ */ jsx(
1034
+ View,
1035
+ {
1036
+ style: [
1037
+ {
1038
+ position: "absolute",
1039
+ top: y,
1040
+ left: 0,
1041
+ right: 0,
1042
+ opacity: 0.6
1043
+ },
1044
+ line.dashed ? {
1045
+ height: 0,
1046
+ borderStyle: "dashed",
1047
+ borderTopWidth: 1,
1048
+ borderTopColor: line.color
1049
+ } : {
1050
+ height: 1,
1051
+ backgroundColor: line.color
1052
+ }
1053
+ ],
1054
+ accessibilityElementsHidden: true,
1055
+ testID: `sparkline-reference-${i}`,
1056
+ children: line.label && /* @__PURE__ */ jsx(
1057
+ Text,
1058
+ {
1059
+ style: {
1060
+ position: "absolute",
1061
+ right: 0,
1062
+ top: -10,
1063
+ fontSize: 7,
1064
+ color: line.color,
1065
+ opacity: 1
1066
+ },
1067
+ testID: `sparkline-reference-label-${i}`,
1068
+ children: line.label
1069
+ }
1070
+ )
1071
+ },
1072
+ `ref-${i}`
1073
+ );
1074
+ }),
1075
+ normalized.map((y, i) => {
1076
+ if (i === 0) return null;
1077
+ const prevY = normalized[i - 1];
1078
+ const x1 = (i - 1) * stepX;
1079
+ const x2 = i * stepX;
1080
+ const dx = x2 - x1;
1081
+ const dy = y - prevY;
1082
+ const length = Math.sqrt(dx * dx + dy * dy);
1083
+ const angle = Math.atan2(dy, dx) * (180 / Math.PI);
1084
+ return /* @__PURE__ */ jsx(
1085
+ View,
1086
+ {
1087
+ style: {
1088
+ position: "absolute",
1089
+ left: x1,
1090
+ top: prevY,
1091
+ width: length,
1092
+ height: 1.5,
1093
+ backgroundColor: resolvedColor,
1094
+ transformOrigin: "0 0",
1095
+ transform: [{ rotate: `${angle}deg` }]
1096
+ },
1097
+ accessibilityElementsHidden: true,
1098
+ testID: `sparkline-segment-${i}`
1099
+ },
1100
+ `line-${i}`
1101
+ );
1102
+ }),
1103
+ (showDots || highlightLast) && normalized.map((y, i) => {
1104
+ const isLast = i === data.length - 1;
1105
+ const shouldShow = showDots || highlightLast && isLast;
1106
+ if (!shouldShow) return null;
1107
+ const size = highlightLast && isLast ? highlightSize : dotSize;
1108
+ return /* @__PURE__ */ jsx(
1109
+ View,
1110
+ {
1111
+ style: {
1112
+ position: "absolute",
1113
+ left: i * stepX - size / 2,
1114
+ top: y - size / 2,
1115
+ width: size,
1116
+ height: size,
1117
+ borderRadius: size / 2,
1118
+ backgroundColor: resolvedColor
1119
+ },
1120
+ accessibilityElementsHidden: true,
1121
+ testID: `sparkline-dot-${i}`
1122
+ },
1123
+ `dot-${i}`
1124
+ );
1125
+ })
1126
+ ]
1127
+ }
1128
+ );
1129
+ }
1130
+
1131
+ // src/components/custom/Workout/BodyMapDetailPanel.tsx
1132
+ var BRAND_PRIMARY2 = "#FF7900";
444
1133
  var VOLUME_GRADIENT = "linear-gradient(90deg, #2196F3 0%, #14B8A6 50%, #D14343 100%)";
445
1134
  var SLIDE_OFFSET = 400;
446
1135
  var BACKDROP_OPACITY = 0.4;
@@ -839,7 +1528,7 @@ function BodyMapDetailPanel({
839
1528
  borderColor: "rgba(255,121,0,0.3)"
840
1529
  },
841
1530
  testID: "body-map-detail-panel-view-exercises",
842
- children: /* @__PURE__ */ jsx(Text, { style: { fontSize: 13, fontFamily: "Inter, sans-serif", fontWeight: "700", color: BRAND_PRIMARY }, children: "View exercises" })
1531
+ children: /* @__PURE__ */ jsx(Text, { style: { fontSize: 13, fontFamily: "Inter, sans-serif", fontWeight: "700", color: BRAND_PRIMARY2 }, children: "View exercises" })
843
1532
  }
844
1533
  )
845
1534
  ]
@@ -852,6 +1541,1222 @@ function BodyMapDetailPanel({
852
1541
  }
853
1542
  );
854
1543
  }
1544
+ function hexToRgb(hex) {
1545
+ const cleaned = hex.replace("#", "");
1546
+ let r, g, b;
1547
+ if (cleaned.length === 3) {
1548
+ r = parseInt(cleaned[0] + cleaned[0], 16);
1549
+ g = parseInt(cleaned[1] + cleaned[1], 16);
1550
+ b = parseInt(cleaned[2] + cleaned[2], 16);
1551
+ } else if (cleaned.length === 6) {
1552
+ r = parseInt(cleaned.slice(0, 2), 16);
1553
+ g = parseInt(cleaned.slice(2, 4), 16);
1554
+ b = parseInt(cleaned.slice(4, 6), 16);
1555
+ } else {
1556
+ return null;
1557
+ }
1558
+ return { r, g, b };
1559
+ }
1560
+ function rgbToHsv(r, g, b) {
1561
+ const rNorm = r / 255;
1562
+ const gNorm = g / 255;
1563
+ const bNorm = b / 255;
1564
+ const max = Math.max(rNorm, gNorm, bNorm);
1565
+ const min = Math.min(rNorm, gNorm, bNorm);
1566
+ const delta = max - min;
1567
+ const v = max;
1568
+ const s = max === 0 ? 0 : delta / max;
1569
+ let h = 0;
1570
+ if (delta !== 0) {
1571
+ if (max === rNorm) {
1572
+ h = 60 * ((gNorm - bNorm) / delta % 6);
1573
+ } else if (max === gNorm) {
1574
+ h = 60 * ((bNorm - rNorm) / delta + 2);
1575
+ } else {
1576
+ h = 60 * ((rNorm - gNorm) / delta + 4);
1577
+ }
1578
+ }
1579
+ if (h < 0) h += 360;
1580
+ return { h, s, v };
1581
+ }
1582
+ function hsvToRgb(h, s, v) {
1583
+ const c = v * s;
1584
+ const x = c * (1 - Math.abs(h / 60 % 2 - 1));
1585
+ const m = v - c;
1586
+ let rPrime, gPrime, bPrime;
1587
+ if (h >= 0 && h < 60) {
1588
+ rPrime = c;
1589
+ gPrime = x;
1590
+ bPrime = 0;
1591
+ } else if (h >= 60 && h < 120) {
1592
+ rPrime = x;
1593
+ gPrime = c;
1594
+ bPrime = 0;
1595
+ } else if (h >= 120 && h < 180) {
1596
+ rPrime = 0;
1597
+ gPrime = c;
1598
+ bPrime = x;
1599
+ } else if (h >= 180 && h < 240) {
1600
+ rPrime = 0;
1601
+ gPrime = x;
1602
+ bPrime = c;
1603
+ } else if (h >= 240 && h < 300) {
1604
+ rPrime = x;
1605
+ gPrime = 0;
1606
+ bPrime = c;
1607
+ } else {
1608
+ rPrime = c;
1609
+ gPrime = 0;
1610
+ bPrime = x;
1611
+ }
1612
+ return {
1613
+ r: Math.round((rPrime + m) * 255),
1614
+ g: Math.round((gPrime + m) * 255),
1615
+ b: Math.round((bPrime + m) * 255)
1616
+ };
1617
+ }
1618
+ function rgbToHex(r, g, b) {
1619
+ const toHex = (n) => {
1620
+ const clamped = Math.max(0, Math.min(255, Math.round(n)));
1621
+ return clamped.toString(16).padStart(2, "0");
1622
+ };
1623
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
1624
+ }
1625
+ function lighten(hex, amount = 0.1) {
1626
+ const rgb = hexToRgb(hex);
1627
+ if (!rgb) return hex;
1628
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
1629
+ const newV = Math.min(1, hsv.v + amount);
1630
+ const newRgb = hsvToRgb(hsv.h, hsv.s, newV);
1631
+ return rgbToHex(newRgb.r, newRgb.g, newRgb.b);
1632
+ }
1633
+ var shadowColors = {
1634
+ // For charcoal surfaces (~#2C2C2C to #3C3C3C)
1635
+ charcoal: {
1636
+ dark: "rgba(0, 0, 0, 0.5)",
1637
+ light: "rgba(255, 255, 255, 0.12)",
1638
+ // brighter for crisp rim light
1639
+ darker: "rgba(0, 0, 0, 0.6)"
1640
+ },
1641
+ // For neutral dark surfaces (~#1F2937)
1642
+ neutralDark: {
1643
+ dark: "rgba(0, 0, 0, 0.4)",
1644
+ light: "rgba(255, 255, 255, 0.08)",
1645
+ darker: "rgba(0, 0, 0, 0.5)"
1646
+ },
1647
+ // For light surfaces (white/light gray backgrounds)
1648
+ neutralLight: {
1649
+ dark: "rgba(0, 0, 0, 0.15)",
1650
+ // Visible dark shadow for depth on light backgrounds
1651
+ light: "rgba(255, 255, 255, 1)",
1652
+ // Bright highlight for raised effect (visible on light gray)
1653
+ darker: "rgba(0, 0, 0, 0.2)"
1654
+ // Deeper shadow for pressed states
1655
+ }
1656
+ };
1657
+ var intensityConfig = {
1658
+ subtle: { offset: 1, blur: 2 },
1659
+ // very light, barely visible
1660
+ medium: { offset: 2, blur: 4 },
1661
+ // noticeable but restrained
1662
+ strong: { offset: 2, blur: 5 }
1663
+ // slightly deeper blur, same offset as medium
1664
+ };
1665
+ function createRaisedShadow(surface = "charcoal", intensity = "medium") {
1666
+ const colors = shadowColors[surface];
1667
+ const { offset, blur } = intensityConfig[intensity];
1668
+ const rimBlur = 1;
1669
+ return Platform.select({
1670
+ web: {
1671
+ boxShadow: `${offset}px ${offset}px ${blur}px ${colors.dark}, -${offset}px -${offset}px ${rimBlur}px ${colors.light}`
1672
+ },
1673
+ default: {
1674
+ // React Native shadow approximation (limited compared to web)
1675
+ shadowColor: "#000",
1676
+ shadowOffset: { width: offset, height: offset },
1677
+ shadowOpacity: 0.3,
1678
+ shadowRadius: blur / 2,
1679
+ elevation: offset
1680
+ }
1681
+ });
1682
+ }
1683
+ function createPressedShadow(surface = "charcoal", intensity = "medium") {
1684
+ const colors = shadowColors[surface];
1685
+ const { offset, blur } = intensityConfig[intensity];
1686
+ const rimOffset = 1;
1687
+ const rimBlur = 1;
1688
+ return Platform.select({
1689
+ web: {
1690
+ boxShadow: `inset ${offset}px ${offset}px ${blur}px ${colors.darker}, inset -${rimOffset}px -${rimOffset}px ${rimBlur}px ${colors.light}`
1691
+ },
1692
+ default: {
1693
+ // React Native doesn't support inset shadows, approximate with darker bg
1694
+ shadowOpacity: 0,
1695
+ elevation: 0
1696
+ }
1697
+ });
1698
+ }
1699
+ function createFlatShadow() {
1700
+ return Platform.select({
1701
+ web: {
1702
+ boxShadow: "none"
1703
+ },
1704
+ default: {
1705
+ shadowOpacity: 0,
1706
+ elevation: 0
1707
+ }
1708
+ });
1709
+ }
1710
+ function createRaisedHoverShadow(surface = "charcoal", intensity = "medium") {
1711
+ const colors = shadowColors[surface];
1712
+ const { offset, blur } = intensityConfig[intensity];
1713
+ const hoverOffset = Math.max(1, Math.round(offset / 2));
1714
+ const hoverBlur = Math.max(1, Math.round(blur / 2));
1715
+ const rimBlur = 1;
1716
+ return Platform.select({
1717
+ web: {
1718
+ // Keep same offsets as normal state for rim to prevent "shrinking" effect
1719
+ boxShadow: `${hoverOffset}px ${hoverOffset}px ${hoverBlur}px ${colors.dark}, -${offset}px -${offset}px ${rimBlur}px ${colors.light}`
1720
+ },
1721
+ default: {
1722
+ shadowColor: "#000",
1723
+ shadowOffset: { width: hoverOffset, height: hoverOffset },
1724
+ shadowOpacity: 0.2,
1725
+ shadowRadius: hoverBlur / 2,
1726
+ elevation: hoverOffset
1727
+ }
1728
+ });
1729
+ }
1730
+ function createPressedHoverShadow(surface = "charcoal", intensity = "medium") {
1731
+ const colors = shadowColors[surface];
1732
+ const { offset, blur } = intensityConfig[intensity];
1733
+ const hoverOffset = Math.max(1, Math.round(offset / 2));
1734
+ const hoverBlur = Math.max(1, Math.round(blur / 2));
1735
+ const rimBlur = 1;
1736
+ return Platform.select({
1737
+ web: {
1738
+ // Keep rim offset at 1px (same as pressed normal) to prevent "shrinking" effect
1739
+ boxShadow: `inset ${hoverOffset}px ${hoverOffset}px ${hoverBlur}px ${colors.darker}, inset -1px -1px ${rimBlur}px ${colors.light}`
1740
+ },
1741
+ default: {
1742
+ shadowOpacity: 0,
1743
+ elevation: 0
1744
+ }
1745
+ });
1746
+ }
1747
+ ({
1748
+ // Charcoal surface shadows (for dark toolbars, cards)
1749
+ charcoal: {
1750
+ raised: {
1751
+ subtle: createRaisedShadow("charcoal", "subtle"),
1752
+ medium: createRaisedShadow("charcoal", "medium"),
1753
+ strong: createRaisedShadow("charcoal", "strong")
1754
+ },
1755
+ raisedHover: {
1756
+ subtle: createRaisedHoverShadow("charcoal", "subtle"),
1757
+ medium: createRaisedHoverShadow("charcoal", "medium"),
1758
+ strong: createRaisedHoverShadow("charcoal", "strong")
1759
+ },
1760
+ pressed: {
1761
+ subtle: createPressedShadow("charcoal", "subtle"),
1762
+ medium: createPressedShadow("charcoal", "medium"),
1763
+ strong: createPressedShadow("charcoal", "strong")
1764
+ },
1765
+ pressedHover: {
1766
+ subtle: createPressedHoverShadow("charcoal", "subtle"),
1767
+ medium: createPressedHoverShadow("charcoal", "medium"),
1768
+ strong: createPressedHoverShadow("charcoal", "strong")
1769
+ },
1770
+ flat: createFlatShadow()
1771
+ },
1772
+ // Neutral dark surface shadows
1773
+ neutralDark: {
1774
+ raised: {
1775
+ subtle: createRaisedShadow("neutralDark", "subtle"),
1776
+ medium: createRaisedShadow("neutralDark", "medium"),
1777
+ strong: createRaisedShadow("neutralDark", "strong")
1778
+ },
1779
+ raisedHover: {
1780
+ subtle: createRaisedHoverShadow("neutralDark", "subtle"),
1781
+ medium: createRaisedHoverShadow("neutralDark", "medium"),
1782
+ strong: createRaisedHoverShadow("neutralDark", "strong")
1783
+ },
1784
+ pressed: {
1785
+ subtle: createPressedShadow("neutralDark", "subtle"),
1786
+ medium: createPressedShadow("neutralDark", "medium"),
1787
+ strong: createPressedShadow("neutralDark", "strong")
1788
+ },
1789
+ pressedHover: {
1790
+ subtle: createPressedHoverShadow("neutralDark", "subtle"),
1791
+ medium: createPressedHoverShadow("neutralDark", "medium"),
1792
+ strong: createPressedHoverShadow("neutralDark", "strong")
1793
+ },
1794
+ flat: createFlatShadow()
1795
+ },
1796
+ // Neutral light surface shadows
1797
+ neutralLight: {
1798
+ raised: {
1799
+ subtle: createRaisedShadow("neutralLight", "subtle"),
1800
+ medium: createRaisedShadow("neutralLight", "medium"),
1801
+ strong: createRaisedShadow("neutralLight", "strong")
1802
+ },
1803
+ raisedHover: {
1804
+ subtle: createRaisedHoverShadow("neutralLight", "subtle"),
1805
+ medium: createRaisedHoverShadow("neutralLight", "medium"),
1806
+ strong: createRaisedHoverShadow("neutralLight", "strong")
1807
+ },
1808
+ pressed: {
1809
+ subtle: createPressedShadow("neutralLight", "subtle"),
1810
+ medium: createPressedShadow("neutralLight", "medium"),
1811
+ strong: createPressedShadow("neutralLight", "strong")
1812
+ },
1813
+ pressedHover: {
1814
+ subtle: createPressedHoverShadow("neutralLight", "subtle"),
1815
+ medium: createPressedHoverShadow("neutralLight", "medium"),
1816
+ strong: createPressedHoverShadow("neutralLight", "strong")
1817
+ },
1818
+ flat: createFlatShadow()
1819
+ }
1820
+ });
1821
+ var elevationSystem = {
1822
+ // Deep inset (recessed input fields, deeply pressed buttons)
1823
+ [-2]: {
1824
+ name: "deep-inset",
1825
+ colorAdjustment: 0,
1826
+ shadowIntensity: "strong",
1827
+ shadowStyle: "pressed",
1828
+ description: "Deeply recessed elements (e.g., input fields, deeply pressed buttons)"
1829
+ },
1830
+ // Shallow inset (pressed buttons, sunken panels)
1831
+ [-1]: {
1832
+ name: "inset",
1833
+ colorAdjustment: 0,
1834
+ shadowIntensity: "medium",
1835
+ shadowStyle: "pressed",
1836
+ description: "Shallow inset elements (e.g., pressed buttons, sunken panels within cards)"
1837
+ },
1838
+ // Base level (page background, default surface)
1839
+ [0]: {
1840
+ name: "base",
1841
+ colorAdjustment: 0,
1842
+ shadowIntensity: "subtle",
1843
+ shadowStyle: "raised",
1844
+ description: "Base surface level (page background, default containers)"
1845
+ },
1846
+ // Level 1: Subtle elevation (outline cards, subtle containers)
1847
+ [1]: {
1848
+ name: "subtle",
1849
+ colorAdjustment: 0.01,
1850
+ shadowIntensity: "subtle",
1851
+ shadowStyle: "raised",
1852
+ description: "Subtle elevation (outline cards, subtle containers)"
1853
+ },
1854
+ // Level 2: Standard elevation (default cards, buttons)
1855
+ [2]: {
1856
+ name: "standard",
1857
+ colorAdjustment: 0.02,
1858
+ shadowIntensity: "medium",
1859
+ shadowStyle: "raised",
1860
+ description: "Standard elevation (default cards, raised buttons)"
1861
+ },
1862
+ // Level 3: Prominent elevation (important cards, modals)
1863
+ [3]: {
1864
+ name: "prominent",
1865
+ colorAdjustment: 0.04,
1866
+ shadowIntensity: "strong",
1867
+ shadowStyle: "raised",
1868
+ description: "Prominent elevation (important cards, modals, dropdowns)"
1869
+ },
1870
+ // Level 4: High elevation (overlays, popovers)
1871
+ [4]: {
1872
+ name: "high",
1873
+ colorAdjustment: 0.06,
1874
+ shadowIntensity: "strong",
1875
+ shadowStyle: "raised",
1876
+ description: "High elevation (overlays, popovers, tooltips)"
1877
+ },
1878
+ // Level 5: Floating (toasts, floating action buttons)
1879
+ [5]: {
1880
+ name: "floating",
1881
+ colorAdjustment: 0.08,
1882
+ shadowIntensity: "strong",
1883
+ shadowStyle: "raised",
1884
+ description: "Floating elevation (toasts, floating action buttons, highest z-index)"
1885
+ }
1886
+ };
1887
+ function calculateRimLightColor(baseColor, level) {
1888
+ const rgb = hexToRgb(baseColor);
1889
+ if (!rgb) {
1890
+ return "rgba(255, 255, 255, 0.5)";
1891
+ }
1892
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
1893
+ const isLight = hsv.v > 0.6;
1894
+ const isSaturated = hsv.s > 0.3;
1895
+ const absLevel = Math.abs(level);
1896
+ const opacityBoost = Math.max(0, (absLevel - 1) * 0.05);
1897
+ if (isSaturated) {
1898
+ const rimLightValue = 0.95;
1899
+ const rimLightRgb = hsvToRgb(hsv.h, hsv.s * 0.3, rimLightValue);
1900
+ const baseOpacity = isLight ? 0.9 : 0.5;
1901
+ const finalOpacity = Math.min(1, baseOpacity + opacityBoost);
1902
+ return `rgba(${rimLightRgb.r}, ${rimLightRgb.g}, ${rimLightRgb.b}, ${finalOpacity.toFixed(2)})`;
1903
+ } else {
1904
+ const baseOpacity = isLight ? 0.9 : 0.12;
1905
+ const finalOpacity = Math.min(1, baseOpacity + opacityBoost);
1906
+ return `rgba(255, 255, 255, ${finalOpacity.toFixed(2)})`;
1907
+ }
1908
+ }
1909
+ function calculateDarkShadowColors(surfaceColor) {
1910
+ const rgb = hexToRgb(surfaceColor);
1911
+ if (!rgb) {
1912
+ return {
1913
+ dark: "rgba(0, 0, 0, 0.3)",
1914
+ darker: "rgba(0, 0, 0, 0.4)"
1915
+ };
1916
+ }
1917
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
1918
+ const isLight = hsv.v > 0.6;
1919
+ const isSaturated = hsv.s > 0.3;
1920
+ if (isSaturated) {
1921
+ const darkShadowValue = 0.15;
1922
+ const darkShadowRgb = hsvToRgb(hsv.h, hsv.s, darkShadowValue);
1923
+ if (isLight) {
1924
+ return {
1925
+ dark: `rgba(${darkShadowRgb.r}, ${darkShadowRgb.g}, ${darkShadowRgb.b}, 0.3)`,
1926
+ darker: `rgba(${darkShadowRgb.r}, ${darkShadowRgb.g}, ${darkShadowRgb.b}, 0.4)`
1927
+ };
1928
+ } else {
1929
+ return {
1930
+ dark: `rgba(${darkShadowRgb.r}, ${darkShadowRgb.g}, ${darkShadowRgb.b}, 0.5)`,
1931
+ darker: `rgba(${darkShadowRgb.r}, ${darkShadowRgb.g}, ${darkShadowRgb.b}, 0.6)`
1932
+ };
1933
+ }
1934
+ } else {
1935
+ if (isLight) {
1936
+ return {
1937
+ dark: "rgba(0, 0, 0, 0.2)",
1938
+ darker: "rgba(0, 0, 0, 0.3)"
1939
+ };
1940
+ } else {
1941
+ return {
1942
+ dark: "rgba(0, 0, 0, 0.5)",
1943
+ darker: "rgba(0, 0, 0, 0.6)"
1944
+ };
1945
+ }
1946
+ }
1947
+ }
1948
+ function getRimLightConfig() {
1949
+ return {
1950
+ offset: 1,
1951
+ // Fixed 1px for crisp rim
1952
+ blur: 1
1953
+ // Tight blur for crisp edge
1954
+ };
1955
+ }
1956
+ var elevationSurfaceCache = /* @__PURE__ */ new Map();
1957
+ var MAX_CACHE_SIZE = 100;
1958
+ function getCacheKey(baseColor, level, theme) {
1959
+ return `${baseColor}-${level}-${theme}`;
1960
+ }
1961
+ function calculateElevationSurface(baseColor, level, theme) {
1962
+ const config = elevationSystem[level];
1963
+ if (config.colorAdjustment === 0) {
1964
+ return baseColor;
1965
+ }
1966
+ if (level < 0) {
1967
+ return baseColor;
1968
+ }
1969
+ return lighten(baseColor, config.colorAdjustment);
1970
+ }
1971
+ function getElevationSurface(baseColor, level, theme) {
1972
+ const cacheKey = getCacheKey(baseColor, level, theme);
1973
+ if (elevationSurfaceCache.has(cacheKey)) {
1974
+ return elevationSurfaceCache.get(cacheKey);
1975
+ }
1976
+ const result = calculateElevationSurface(baseColor, level);
1977
+ if (elevationSurfaceCache.size >= MAX_CACHE_SIZE) {
1978
+ const firstKey = elevationSurfaceCache.keys().next().value;
1979
+ elevationSurfaceCache.delete(firstKey);
1980
+ }
1981
+ elevationSurfaceCache.set(cacheKey, result);
1982
+ return result;
1983
+ }
1984
+ var elevationShadowCache = /* @__PURE__ */ new Map();
1985
+ var MAX_SHADOW_CACHE_SIZE = 50;
1986
+ function getShadowCacheKey(baseColor, level, theme, isHovered) {
1987
+ return `${baseColor}-${level}-${theme}-${isHovered}`;
1988
+ }
1989
+ function calculateElevationShadow(baseColor, level, theme, isHovered = false) {
1990
+ const config = elevationSystem[level];
1991
+ if (level === 0) {
1992
+ return {};
1993
+ }
1994
+ const elevatedSurfaceColor = calculateElevationSurface(baseColor, level);
1995
+ const rimLightColor = calculateRimLightColor(baseColor, level);
1996
+ const darkShadowColors = calculateDarkShadowColors(elevatedSurfaceColor);
1997
+ const intensityConfig2 = {
1998
+ subtle: { offset: 1, blur: 2 },
1999
+ medium: { offset: 2, blur: 4 },
2000
+ strong: { offset: 2, blur: 5 }
2001
+ };
2002
+ const { offset, blur } = intensityConfig2[config.shadowIntensity];
2003
+ const rimConfig = getRimLightConfig();
2004
+ if (config.shadowStyle === "pressed") {
2005
+ if (isHovered) {
2006
+ const hoverOffset = Math.max(1, Math.round(offset / 2));
2007
+ const hoverBlur = Math.max(1, Math.round(blur / 2));
2008
+ return Platform.select({
2009
+ web: {
2010
+ boxShadow: `inset ${hoverOffset}px ${hoverOffset}px ${hoverBlur}px ${darkShadowColors.darker}, inset -${rimConfig.offset}px -${rimConfig.offset}px ${rimConfig.blur}px ${rimLightColor}`
2011
+ },
2012
+ default: {
2013
+ shadowOpacity: 0,
2014
+ elevation: 0
2015
+ }
2016
+ });
2017
+ }
2018
+ return Platform.select({
2019
+ web: {
2020
+ boxShadow: `inset ${offset}px ${offset}px ${blur}px ${darkShadowColors.darker}, inset -${rimConfig.offset}px -${rimConfig.offset}px ${rimConfig.blur}px ${rimLightColor}`
2021
+ },
2022
+ default: {
2023
+ shadowOpacity: 0,
2024
+ elevation: 0
2025
+ }
2026
+ });
2027
+ } else {
2028
+ if (isHovered) {
2029
+ const hoverOffset = Math.max(1, Math.round(offset / 2));
2030
+ const hoverBlur = Math.max(1, Math.round(blur / 2));
2031
+ return Platform.select({
2032
+ web: {
2033
+ boxShadow: `${hoverOffset}px ${hoverOffset}px ${hoverBlur}px ${darkShadowColors.dark}, -${rimConfig.offset}px -${rimConfig.offset}px ${rimConfig.blur}px ${rimLightColor}`
2034
+ },
2035
+ default: {
2036
+ shadowColor: "#000",
2037
+ shadowOffset: { width: hoverOffset, height: hoverOffset },
2038
+ shadowOpacity: 0.2,
2039
+ shadowRadius: hoverBlur / 2,
2040
+ elevation: hoverOffset
2041
+ }
2042
+ });
2043
+ }
2044
+ return Platform.select({
2045
+ web: {
2046
+ boxShadow: `${offset}px ${offset}px ${blur}px ${darkShadowColors.dark}, -${rimConfig.offset}px -${rimConfig.offset}px ${rimConfig.blur}px ${rimLightColor}`
2047
+ },
2048
+ default: {
2049
+ shadowColor: "#000",
2050
+ shadowOffset: { width: offset, height: offset },
2051
+ shadowOpacity: 0.3,
2052
+ shadowRadius: blur / 2,
2053
+ elevation: offset
2054
+ }
2055
+ });
2056
+ }
2057
+ }
2058
+ function getElevationShadow(baseColor, level, theme, isHovered = false) {
2059
+ const cacheKey = getShadowCacheKey(baseColor, level, theme, isHovered);
2060
+ if (elevationShadowCache.has(cacheKey)) {
2061
+ return elevationShadowCache.get(cacheKey);
2062
+ }
2063
+ const result = calculateElevationShadow(baseColor, level, theme, isHovered);
2064
+ if (elevationShadowCache.size >= MAX_SHADOW_CACHE_SIZE) {
2065
+ const firstKey = elevationShadowCache.keys().next().value;
2066
+ elevationShadowCache.delete(firstKey);
2067
+ }
2068
+ elevationShadowCache.set(cacheKey, result);
2069
+ return result;
2070
+ }
2071
+ function getBaseSurfaceColor(theme) {
2072
+ return theme === "light" ? semanticColorsLight["surface-raised"] : semanticColorsDark["surface-elevated"];
2073
+ }
2074
+ var componentElevationRanges = {
2075
+ button: {
2076
+ allowed: [2],
2077
+ default: 2,
2078
+ hover: 3
2079
+ },
2080
+ card: {
2081
+ allowed: [1, 2, 3],
2082
+ default: 2,
2083
+ hover: 3
2084
+ },
2085
+ input: {
2086
+ allowed: [-2, -1, 0],
2087
+ default: -1
2088
+ },
2089
+ overlay: {
2090
+ allowed: [4, 5],
2091
+ default: 4
2092
+ }
2093
+ };
2094
+ function getValidatedElevation(component, requested) {
2095
+ const range = componentElevationRanges[component];
2096
+ if (range.allowed.includes(requested)) {
2097
+ return requested;
2098
+ }
2099
+ const sorted = [...range.allowed].sort((a, b) => a - b);
2100
+ if (requested < sorted[0]) return sorted[0];
2101
+ if (requested > sorted[sorted.length - 1]) return sorted[sorted.length - 1];
2102
+ return sorted.reduce(
2103
+ (prev, curr) => Math.abs(curr - requested) < Math.abs(prev - requested) ? curr : prev
2104
+ );
2105
+ }
2106
+ function getTheme() {
2107
+ if (typeof document === "undefined") return "dark";
2108
+ return document.documentElement.classList.contains("light") ? "light" : "dark";
2109
+ }
2110
+ var listeners = [];
2111
+ function subscribe(listener) {
2112
+ listeners = [...listeners, listener];
2113
+ if (typeof document !== "undefined" && listeners.length === 1) {
2114
+ const observer = new MutationObserver((mutations) => {
2115
+ for (const mutation of mutations) {
2116
+ if (mutation.type === "attributes" && mutation.attributeName === "class") {
2117
+ listeners.forEach((l) => l());
2118
+ }
2119
+ }
2120
+ });
2121
+ observer.observe(document.documentElement, {
2122
+ attributes: true,
2123
+ attributeFilter: ["class"]
2124
+ });
2125
+ subscribe._observer = observer;
2126
+ }
2127
+ return () => {
2128
+ listeners = listeners.filter((l) => l !== listener);
2129
+ if (listeners.length === 0 && subscribe._observer) {
2130
+ subscribe._observer.disconnect();
2131
+ subscribe._observer = null;
2132
+ }
2133
+ };
2134
+ }
2135
+ function getSnapshot() {
2136
+ return getTheme();
2137
+ }
2138
+ function getServerSnapshot() {
2139
+ return "dark";
2140
+ }
2141
+ function useTheme() {
2142
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
2143
+ }
2144
+
2145
+ // src/components/ui/card/Card.tsx
2146
+ var variantStyles = {
2147
+ elevated: "",
2148
+ // Will be set dynamically via elevation system
2149
+ outline: "border-2 border-border-strong",
2150
+ // Thicker border with stronger contrast
2151
+ filled: "",
2152
+ // Will be set dynamically via elevation system
2153
+ accent: "border border-border",
2154
+ subtle: "border border-border-subtle"
2155
+ };
2156
+ function Card({
2157
+ variant = "elevated",
2158
+ elevation = 2,
2159
+ isInteractive = false,
2160
+ isLoading = false,
2161
+ onPress,
2162
+ borderColor,
2163
+ bgColor,
2164
+ accentColor,
2165
+ accentWidth,
2166
+ className,
2167
+ children,
2168
+ style,
2169
+ skeletonHasHeader = true,
2170
+ skeletonHasFooter = false,
2171
+ skeletonContentLines = 3,
2172
+ ...props
2173
+ }) {
2174
+ const isClickable = isInteractive || !!onPress;
2175
+ const [isHovered, setIsHovered] = useState(false);
2176
+ const theme = useTheme();
2177
+ const elevationLevel = useMemo(() => {
2178
+ const validated = getValidatedElevation("card", elevation);
2179
+ if (variant === "outline" || variant === "accent" || variant === "subtle") {
2180
+ return 1;
2181
+ }
2182
+ return validated;
2183
+ }, [variant, elevation]);
2184
+ const baseColor = useMemo(() => getBaseSurfaceColor(theme), [theme]);
2185
+ const surfaceColor = useMemo(() => {
2186
+ if ((variant === "outline" || variant === "accent") && theme === "light") {
2187
+ return "#FAFAFA";
2188
+ }
2189
+ if (variant === "subtle") {
2190
+ return getElevationSurface(baseColor, 0, theme);
2191
+ }
2192
+ return getElevationSurface(baseColor, elevationLevel, theme);
2193
+ }, [baseColor, elevationLevel, theme, variant]);
2194
+ const shadowStyle = useMemo(() => {
2195
+ const shouldHover = isHovered && isClickable;
2196
+ return getElevationShadow(baseColor, elevationLevel, theme, shouldHover);
2197
+ }, [baseColor, elevationLevel, theme, isHovered, isClickable]);
2198
+ const cardContent = isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
2199
+ skeletonHasHeader && /* @__PURE__ */ jsxs(CardHeader, { children: [
2200
+ /* @__PURE__ */ jsx(View, { className: "h-5 w-1/3 bg-interactive-disabled rounded" }),
2201
+ /* @__PURE__ */ jsx(View, { className: "mt-2 h-4 w-2/3 bg-interactive-disabled rounded" })
2202
+ ] }),
2203
+ /* @__PURE__ */ jsx(CardContent, { children: Array.from({ length: skeletonContentLines }).map((_, i) => /* @__PURE__ */ jsx(
2204
+ View,
2205
+ {
2206
+ className: cn(
2207
+ "h-4 bg-interactive-disabled rounded",
2208
+ i < skeletonContentLines - 1 ? "mb-2 w-full" : "w-4/5"
2209
+ )
2210
+ },
2211
+ i
2212
+ )) }),
2213
+ skeletonHasFooter && /* @__PURE__ */ jsx(CardFooter, { children: /* @__PURE__ */ jsx(View, { className: "h-9 w-24 bg-interactive-disabled rounded" }) })
2214
+ ] }) : children;
2215
+ const baseClassName = cn(
2216
+ "rounded-lg overflow-hidden relative",
2217
+ // Apply variant styles, but only use default border color if no custom borderColor
2218
+ variant === "outline" ? borderColor ? "border-2" : variantStyles[variant] : variantStyles[variant],
2219
+ // Remove Tailwind shadow classes - we're using elevation shadows via style prop
2220
+ isClickable && "web:cursor-pointer web:transition-all web:duration-150",
2221
+ isClickable && "web:hover:-translate-y-0.5 active:scale-[0.99]",
2222
+ isLoading && "pointer-events-none animate-pulse",
2223
+ className
2224
+ );
2225
+ const accentStyle = useMemo(() => variant === "accent" ? {
2226
+ borderLeftWidth: accentWidth ?? 3,
2227
+ borderLeftColor: accentColor ?? "var(--color-brand-primary)"
2228
+ } : {}, [variant, accentWidth, accentColor]);
2229
+ const mergedStyle = useMemo(() => {
2230
+ const elevationStyle = {
2231
+ backgroundColor: bgColor || surfaceColor,
2232
+ borderRadius: 8,
2233
+ overflow: "hidden",
2234
+ ...shadowStyle,
2235
+ ...accentStyle
2236
+ };
2237
+ if (borderColor) {
2238
+ elevationStyle.borderColor = borderColor;
2239
+ }
2240
+ if (!style) return elevationStyle;
2241
+ return [elevationStyle, style];
2242
+ }, [style, surfaceColor, shadowStyle, borderColor, bgColor, accentStyle]);
2243
+ if (isClickable) {
2244
+ return /* @__PURE__ */ jsx(
2245
+ Pressable,
2246
+ {
2247
+ onPress,
2248
+ onHoverIn: () => setIsHovered(true),
2249
+ onHoverOut: () => setIsHovered(false),
2250
+ accessibilityRole: "button",
2251
+ className: baseClassName,
2252
+ style: mergedStyle,
2253
+ ...props,
2254
+ children: cardContent
2255
+ }
2256
+ );
2257
+ }
2258
+ return /* @__PURE__ */ jsx(View, { className: baseClassName, style: mergedStyle, ...props, children: cardContent });
2259
+ }
2260
+ function CardHeader({ children, className }) {
2261
+ return /* @__PURE__ */ jsx(View, { className: cn("px-6 py-6", className), children });
2262
+ }
2263
+ function CardContent({ children, className }) {
2264
+ return /* @__PURE__ */ jsx(View, { className: cn("px-6 py-4", className), children });
2265
+ }
2266
+ function CardFooter({ children, className }) {
2267
+ return /* @__PURE__ */ jsx(View, { className: cn("px-6 py-4 flex-row items-center gap-2", className), children });
2268
+ }
2269
+ var solidVariantColors = {
2270
+ success: "#14B8A6",
2271
+ warning: "#FFB020",
2272
+ error: "#D14343",
2273
+ neutral: "#6B7280"
2274
+ };
2275
+ var ringVariantStyles = {
2276
+ "on-track": {
2277
+ backgroundColor: "rgba(20,184,166,0.15)",
2278
+ borderWidth: 1,
2279
+ borderColor: "rgba(20,184,166,0.3)"
2280
+ },
2281
+ deviation: {
2282
+ backgroundColor: "rgba(245,158,11,0.15)",
2283
+ borderWidth: 1,
2284
+ borderColor: "rgba(245,158,11,0.25)"
2285
+ },
2286
+ future: {
2287
+ backgroundColor: "rgba(107,114,128,0.1)",
2288
+ borderWidth: 1,
2289
+ borderStyle: "dashed",
2290
+ borderColor: "rgba(107,114,128,0.2)"
2291
+ }
2292
+ };
2293
+ var glowStyles = {
2294
+ success: { boxShadow: "0 0 4px rgba(20,184,166,0.4)" },
2295
+ warning: { boxShadow: "0 0 4px rgba(245,158,11,0.4)" },
2296
+ error: { boxShadow: "0 0 4px rgba(239,68,68,0.4)" },
2297
+ neutral: { boxShadow: "0 0 4px rgba(107,114,128,0.4)" },
2298
+ "on-track": { boxShadow: "0 0 4px rgba(20,184,166,0.4)" },
2299
+ deviation: { boxShadow: "0 0 4px rgba(245,158,11,0.4)" },
2300
+ future: { boxShadow: "0 0 4px rgba(107,114,128,0.4)" }
2301
+ };
2302
+ var iconChars = {
2303
+ check: "\u2713",
2304
+ exclamation: "!",
2305
+ dash: "\u2014"
2306
+ };
2307
+ var iconColors = {
2308
+ success: "#0A5C52",
2309
+ warning: "#6B4000",
2310
+ error: "#5C1A1A",
2311
+ neutral: "#D1D5DB",
2312
+ "on-track": "#14B8A6",
2313
+ deviation: "#FFB020",
2314
+ future: "#6B7280"
2315
+ };
2316
+ var sizeDimensions = {
2317
+ sm: { size: 8, showIcon: false },
2318
+ md: { size: 18, showIcon: true }
2319
+ };
2320
+ function isSolidVariant(variant) {
2321
+ return variant in solidVariantColors;
2322
+ }
2323
+ function StatusDot({
2324
+ variant,
2325
+ icon,
2326
+ size = "sm",
2327
+ glow,
2328
+ label,
2329
+ className,
2330
+ ...props
2331
+ }) {
2332
+ const config = sizeDimensions[size];
2333
+ const solid = isSolidVariant(variant);
2334
+ const ring = ringVariantStyles[variant];
2335
+ const glowStyle = glow ? glowStyles[variant] : null;
2336
+ const dot = /* @__PURE__ */ jsx(
2337
+ View,
2338
+ {
2339
+ style: [
2340
+ {
2341
+ width: config.size,
2342
+ height: config.size,
2343
+ borderRadius: 9999,
2344
+ alignItems: "center",
2345
+ justifyContent: "center",
2346
+ flexShrink: 0
2347
+ },
2348
+ solid ? { backgroundColor: solidVariantColors[variant] } : void 0,
2349
+ ring ? ring : void 0,
2350
+ glowStyle ? glowStyle : void 0
2351
+ ],
2352
+ accessibilityRole: "image",
2353
+ accessibilityLabel: `${variant} status`,
2354
+ testID: "status-dot",
2355
+ children: config.showIcon && icon && /* @__PURE__ */ jsx(
2356
+ Text,
2357
+ {
2358
+ style: {
2359
+ fontSize: 11,
2360
+ lineHeight: 11,
2361
+ fontWeight: "900",
2362
+ color: iconColors[variant]
2363
+ },
2364
+ accessibilityElementsHidden: true,
2365
+ children: iconChars[icon]
2366
+ }
2367
+ )
2368
+ }
2369
+ );
2370
+ if (label) {
2371
+ return /* @__PURE__ */ jsxs(
2372
+ View,
2373
+ {
2374
+ className,
2375
+ style: { flexDirection: "row", alignItems: "center", gap: 6 },
2376
+ accessibilityLabel: `${variant} status: ${label}`,
2377
+ ...props,
2378
+ children: [
2379
+ /* @__PURE__ */ jsx(View, { accessibilityElementsHidden: true, children: dot }),
2380
+ /* @__PURE__ */ jsx(
2381
+ Text,
2382
+ {
2383
+ style: { fontSize: 12, lineHeight: 16, color: "#9CA3AF" },
2384
+ accessibilityElementsHidden: true,
2385
+ children: label
2386
+ }
2387
+ )
2388
+ ]
2389
+ }
2390
+ );
2391
+ }
2392
+ return /* @__PURE__ */ jsx(View, { className, ...props, children: dot });
2393
+ }
2394
+
2395
+ // src/components/custom/Workout/MesoStatusCard.tsx
2396
+ var BRAND_PRIMARY3 = "#FF7900";
2397
+ var BRAND_PRIMARY_DARK = "#C45E00";
2398
+ var BRAND_PRIMARY_LIGHT = "#FFB066";
2399
+ var SUCCESS = "#14B8A6";
2400
+ var WARNING = "#FFB020";
2401
+ var ERROR = "#D14343";
2402
+ var ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY3, BRAND_PRIMARY_LIGHT];
2403
+ var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
2404
+ 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%)";
2405
+ var STATUS_VARIANTS = {
2406
+ success: { bg: "rgba(20,184,166,0.15)", border: "rgba(20,184,166,0.3)", text: SUCCESS },
2407
+ warning: { bg: "rgba(255,176,32,0.15)", border: "rgba(255,176,32,0.3)", text: WARNING },
2408
+ error: { bg: "rgba(209,67,67,0.15)", border: "rgba(209,67,67,0.25)", text: ERROR }
2409
+ };
2410
+ function clamp012(value) {
2411
+ if (!Number.isFinite(value)) return 0;
2412
+ if (value < 0) return 0;
2413
+ if (value > 1) return 1;
2414
+ return value;
2415
+ }
2416
+ function getGaugeZoneColor(level) {
2417
+ if (level >= 0.7) return ERROR;
2418
+ if (level >= 0.4) return WARNING;
2419
+ return SUCCESS;
2420
+ }
2421
+ function renderCoachingText(text, highlights) {
2422
+ if (!highlights || highlights.length === 0) return text;
2423
+ const escaped = highlights.filter(Boolean).map((h) => h.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
2424
+ if (escaped.length === 0) return text;
2425
+ const regex = new RegExp(`(${escaped.join("|")})`, "g");
2426
+ return text.split(regex).map(
2427
+ (part, index) => highlights.includes(part) ? /* @__PURE__ */ jsx(
2428
+ Text,
2429
+ {
2430
+ className: "text-text-primary",
2431
+ style: { fontWeight: "700" },
2432
+ children: part
2433
+ },
2434
+ `${part}-${index}`
2435
+ ) : /* @__PURE__ */ jsx(Fragment$1, { children: part }, `t-${index}`)
2436
+ );
2437
+ }
2438
+ function StatusPill({ badge }) {
2439
+ const colors = STATUS_VARIANTS[badge.variant];
2440
+ return /* @__PURE__ */ jsxs(
2441
+ View,
2442
+ {
2443
+ className: "flex-row items-center",
2444
+ style: {
2445
+ gap: 5,
2446
+ paddingVertical: 3,
2447
+ paddingHorizontal: 8,
2448
+ borderRadius: 4,
2449
+ borderWidth: 1,
2450
+ backgroundColor: colors.bg,
2451
+ borderColor: colors.border
2452
+ },
2453
+ testID: "meso-status-card-badge",
2454
+ children: [
2455
+ /* @__PURE__ */ jsx(View, { accessibilityElementsHidden: true, children: /* @__PURE__ */ jsx(StatusDot, { variant: badge.variant, size: "sm" }) }),
2456
+ /* @__PURE__ */ jsx(
2457
+ Text,
2458
+ {
2459
+ style: {
2460
+ fontSize: 10,
2461
+ fontFamily: "Inter, sans-serif",
2462
+ fontWeight: "700",
2463
+ letterSpacing: 0.3,
2464
+ color: colors.text
2465
+ },
2466
+ children: badge.label
2467
+ }
2468
+ )
2469
+ ]
2470
+ }
2471
+ );
2472
+ }
2473
+ function MetricCell({ metric }) {
2474
+ return /* @__PURE__ */ jsxs(View, { style: { width: "48%" }, testID: "meso-status-card-metric", children: [
2475
+ /* @__PURE__ */ jsx(
2476
+ Text,
2477
+ {
2478
+ className: "text-text-tertiary",
2479
+ style: {
2480
+ fontSize: 10,
2481
+ fontFamily: "Inter, sans-serif",
2482
+ fontWeight: "600",
2483
+ letterSpacing: 0.5,
2484
+ textTransform: "uppercase"
2485
+ },
2486
+ children: metric.label
2487
+ }
2488
+ ),
2489
+ /* @__PURE__ */ jsx(
2490
+ Text,
2491
+ {
2492
+ className: "text-text-primary",
2493
+ style: {
2494
+ marginTop: 2,
2495
+ fontSize: 13,
2496
+ fontFamily: "Inter, sans-serif",
2497
+ fontWeight: "600"
2498
+ },
2499
+ children: metric.value
2500
+ }
2501
+ )
2502
+ ] });
2503
+ }
2504
+ function Gauge({ gauge }) {
2505
+ const level = clamp012(gauge.level);
2506
+ const percentage = Math.round(level * 100);
2507
+ const markerColor = getGaugeZoneColor(level);
2508
+ return /* @__PURE__ */ jsxs(
2509
+ View,
2510
+ {
2511
+ style: { gap: 6 },
2512
+ accessibilityRole: "progressbar",
2513
+ accessibilityValue: { min: 0, max: 100, now: percentage },
2514
+ accessibilityLabel: `${gauge.label}: ${percentage}%`,
2515
+ "aria-valuenow": percentage,
2516
+ "aria-valuemin": 0,
2517
+ "aria-valuemax": 100,
2518
+ testID: "meso-status-card-gauge",
2519
+ children: [
2520
+ /* @__PURE__ */ jsxs(
2521
+ View,
2522
+ {
2523
+ className: "flex-row items-center justify-between",
2524
+ accessibilityElementsHidden: true,
2525
+ children: [
2526
+ /* @__PURE__ */ jsx(
2527
+ Text,
2528
+ {
2529
+ className: "text-text-secondary",
2530
+ style: {
2531
+ fontSize: 10,
2532
+ fontFamily: "Inter, sans-serif",
2533
+ fontWeight: "500",
2534
+ letterSpacing: 0.5,
2535
+ textTransform: "uppercase"
2536
+ },
2537
+ children: gauge.label
2538
+ }
2539
+ ),
2540
+ gauge.sublabel != null && /* @__PURE__ */ jsx(
2541
+ Text,
2542
+ {
2543
+ className: "text-text-tertiary",
2544
+ style: {
2545
+ fontSize: 10,
2546
+ fontFamily: "Inter, sans-serif",
2547
+ fontWeight: "500"
2548
+ },
2549
+ children: gauge.sublabel
2550
+ }
2551
+ )
2552
+ ]
2553
+ }
2554
+ ),
2555
+ /* @__PURE__ */ jsxs(
2556
+ View,
2557
+ {
2558
+ style: {
2559
+ height: 8,
2560
+ borderRadius: 4,
2561
+ position: "relative",
2562
+ backgroundImage: GAUGE_GRADIENT
2563
+ },
2564
+ accessibilityElementsHidden: true,
2565
+ children: [
2566
+ /* @__PURE__ */ jsx(
2567
+ View,
2568
+ {
2569
+ style: {
2570
+ position: "absolute",
2571
+ left: "50%",
2572
+ top: 0,
2573
+ bottom: 0,
2574
+ width: 1,
2575
+ marginLeft: -0.5,
2576
+ backgroundColor: "rgba(255,255,255,0.3)"
2577
+ },
2578
+ testID: "meso-status-card-gauge-center"
2579
+ }
2580
+ ),
2581
+ /* @__PURE__ */ jsx(
2582
+ View,
2583
+ {
2584
+ className: "border-text-primary",
2585
+ style: {
2586
+ position: "absolute",
2587
+ left: `${percentage}%`,
2588
+ top: -3,
2589
+ marginLeft: -7,
2590
+ width: 14,
2591
+ height: 14,
2592
+ borderRadius: 9999,
2593
+ borderWidth: 2,
2594
+ backgroundColor: markerColor,
2595
+ boxShadow: "0 0 4px rgba(0,0,0,0.5)"
2596
+ },
2597
+ testID: "meso-status-card-gauge-marker"
2598
+ }
2599
+ )
2600
+ ]
2601
+ }
2602
+ )
2603
+ ]
2604
+ }
2605
+ );
2606
+ }
2607
+ function MesoStatusCard({
2608
+ mesoName,
2609
+ mesoSubtitle,
2610
+ statusBadge,
2611
+ metrics,
2612
+ gauges,
2613
+ coaching,
2614
+ nextTarget,
2615
+ className,
2616
+ ...props
2617
+ }) {
2618
+ return /* @__PURE__ */ jsxs(
2619
+ Card,
2620
+ {
2621
+ variant: "outline",
2622
+ elevation: 1,
2623
+ borderColor: BRAND_PRIMARY_DARK,
2624
+ className,
2625
+ style: {
2626
+ borderColor: BRAND_PRIMARY_DARK,
2627
+ borderWidth: 1,
2628
+ backgroundImage: CARD_GRADIENT
2629
+ },
2630
+ role: "article",
2631
+ "aria-label": `Mesocycle status for ${mesoName}`,
2632
+ testID: "meso-status-card",
2633
+ ...props,
2634
+ children: [
2635
+ /* @__PURE__ */ jsx(
2636
+ View,
2637
+ {
2638
+ className: "flex-row",
2639
+ style: { position: "absolute", top: 0, left: 0, right: 0, height: 3, zIndex: 1 },
2640
+ accessibilityElementsHidden: true,
2641
+ testID: "meso-status-card-accent",
2642
+ children: ACCENT_STOPS.map((color) => /* @__PURE__ */ jsx(View, { style: { flex: 1, backgroundColor: color } }, color))
2643
+ }
2644
+ ),
2645
+ /* @__PURE__ */ jsxs(View, { style: { padding: 14, gap: 14 }, testID: "meso-status-card-body", children: [
2646
+ /* @__PURE__ */ jsxs(View, { testID: "meso-status-card-header", children: [
2647
+ /* @__PURE__ */ jsxs(
2648
+ View,
2649
+ {
2650
+ className: "flex-row items-center justify-between",
2651
+ style: { gap: 8 },
2652
+ children: [
2653
+ /* @__PURE__ */ jsx(
2654
+ Text,
2655
+ {
2656
+ className: "text-text-primary",
2657
+ style: {
2658
+ flexShrink: 1,
2659
+ fontSize: 15,
2660
+ fontFamily: '"Space Grotesk", sans-serif',
2661
+ fontWeight: "700"
2662
+ },
2663
+ testID: "meso-status-card-name",
2664
+ children: mesoName
2665
+ }
2666
+ ),
2667
+ /* @__PURE__ */ jsx(StatusPill, { badge: statusBadge })
2668
+ ]
2669
+ }
2670
+ ),
2671
+ /* @__PURE__ */ jsx(
2672
+ Text,
2673
+ {
2674
+ className: "text-text-secondary",
2675
+ style: {
2676
+ marginTop: 4,
2677
+ fontSize: 12,
2678
+ fontFamily: "Inter, sans-serif"
2679
+ },
2680
+ testID: "meso-status-card-subtitle",
2681
+ children: mesoSubtitle
2682
+ }
2683
+ )
2684
+ ] }),
2685
+ metrics.length > 0 && /* @__PURE__ */ jsx(
2686
+ View,
2687
+ {
2688
+ className: "flex-row flex-wrap",
2689
+ style: { gap: 8 },
2690
+ testID: "meso-status-card-metrics",
2691
+ children: metrics.map((metric, index) => /* @__PURE__ */ jsx(MetricCell, { metric }, `${metric.label}-${index}`))
2692
+ }
2693
+ ),
2694
+ 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}`)) }),
2695
+ coaching != null && /* @__PURE__ */ jsx(
2696
+ View,
2697
+ {
2698
+ style: {
2699
+ backgroundColor: "rgba(255,176,32,0.06)",
2700
+ borderWidth: 1,
2701
+ borderColor: "rgba(255,176,32,0.15)",
2702
+ borderRadius: 8,
2703
+ paddingVertical: 10,
2704
+ paddingHorizontal: 12
2705
+ },
2706
+ testID: "meso-status-card-coaching",
2707
+ children: /* @__PURE__ */ jsx(
2708
+ Text,
2709
+ {
2710
+ className: "text-text-secondary",
2711
+ style: {
2712
+ fontSize: 12,
2713
+ lineHeight: 17,
2714
+ fontFamily: "Inter, sans-serif"
2715
+ },
2716
+ children: renderCoachingText(coaching.text, coaching.highlights)
2717
+ }
2718
+ )
2719
+ }
2720
+ ),
2721
+ nextTarget != null && /* @__PURE__ */ jsxs(
2722
+ View,
2723
+ {
2724
+ className: "flex-row items-center",
2725
+ style: {
2726
+ gap: 8,
2727
+ backgroundColor: "rgba(20,184,166,0.06)",
2728
+ borderWidth: 1,
2729
+ borderColor: "rgba(20,184,166,0.2)",
2730
+ borderRadius: 8,
2731
+ paddingVertical: 10,
2732
+ paddingHorizontal: 12
2733
+ },
2734
+ testID: "meso-status-card-next-target",
2735
+ children: [
2736
+ /* @__PURE__ */ jsx(Text, { style: { fontSize: 14, color: SUCCESS }, accessibilityElementsHidden: true, children: nextTarget.icon }),
2737
+ /* @__PURE__ */ jsx(
2738
+ Text,
2739
+ {
2740
+ style: {
2741
+ flexShrink: 1,
2742
+ fontSize: 12,
2743
+ fontFamily: "Inter, sans-serif",
2744
+ fontWeight: "600",
2745
+ color: SUCCESS
2746
+ },
2747
+ children: nextTarget.text
2748
+ }
2749
+ )
2750
+ ]
2751
+ }
2752
+ )
2753
+ ] })
2754
+ ]
2755
+ }
2756
+ );
2757
+ }
2758
+
2759
+ // src/components/custom/Workout/TrainingStatusPage.tsx
855
2760
  var STATUS_ORDER = ["under", "maintenance", "productive", "over"];
856
2761
  function deriveTrainingSummary(muscles) {
857
2762
  const statusCounts = {