@titan-design/react-ui 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +109 -12
- package/dist/index.d.ts +109 -12
- package/dist/index.js +648 -231
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +648 -232
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-8u_4WbL-.d.mts → pages-DCFBqp79.d.mts} +16 -7
- package/dist/{pages-DaWiBOGa.d.ts → pages-_fI3-x7Z.d.ts} +16 -7
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +495 -92
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +496 -93
- package/dist/pages.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +90 -0
- package/src/components/custom/CircularTimer/CircularTimer.test.tsx +62 -0
- package/src/components/custom/CircularTimer/CircularTimer.tsx +112 -0
- package/src/components/custom/CircularTimer/index.ts +1 -0
- package/src/components/custom/Workout/FatigueMeter.stories.tsx +2 -2
- package/src/components/custom/Workout/FatigueMeter.test.tsx +12 -0
- package/src/components/custom/Workout/FatigueMeter.tsx +7 -3
- package/src/components/custom/Workout/README.md +47 -0
- package/src/components/custom/Workout/RestTimer.stories.tsx +132 -2
- package/src/components/custom/Workout/RestTimer.test.tsx +73 -0
- package/src/components/custom/Workout/RestTimer.tsx +141 -50
- package/src/components/custom/Workout/TempoBar.stories.tsx +69 -0
- package/src/components/custom/Workout/TempoBar.test.tsx +119 -4
- package/src/components/custom/Workout/TempoBar.tsx +107 -22
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +152 -9
- package/src/components/custom/Workout/VelocityStrip.test.tsx +64 -0
- package/src/components/custom/Workout/VelocityStrip.tsx +287 -40
- package/src/components/custom/Workout/ZoneTrack.stories.tsx +2 -3
- package/src/components/custom/Workout/ZoneTrack.test.tsx +34 -0
- package/src/components/custom/Workout/ZoneTrack.tsx +76 -18
- package/src/components/custom/index.ts +1 -0
- package/src/components/ui/alert/Alert.stories.tsx +85 -2
- package/src/components/ui/alert/Alert.test.tsx +52 -0
- package/src/components/ui/alert/Alert.tsx +54 -20
- package/src/components/ui/progress/Progress.tsx +47 -22
package/dist/index.js
CHANGED
|
@@ -158,6 +158,7 @@ var statusColors = {
|
|
|
158
158
|
subtle: "bg-status-success-subtle",
|
|
159
159
|
outline: "border-2 border-status-success bg-transparent",
|
|
160
160
|
solid: "bg-status-success",
|
|
161
|
+
border: "border-status-success",
|
|
161
162
|
icon: "text-status-success",
|
|
162
163
|
text: "text-status-success"
|
|
163
164
|
},
|
|
@@ -165,6 +166,7 @@ var statusColors = {
|
|
|
165
166
|
subtle: "bg-status-info-subtle",
|
|
166
167
|
outline: "border-2 border-status-info bg-transparent",
|
|
167
168
|
solid: "bg-status-info",
|
|
169
|
+
border: "border-status-info",
|
|
168
170
|
icon: "text-status-info",
|
|
169
171
|
text: "text-status-info"
|
|
170
172
|
},
|
|
@@ -172,6 +174,7 @@ var statusColors = {
|
|
|
172
174
|
subtle: "bg-status-warning-subtle",
|
|
173
175
|
outline: "border-2 border-status-warning bg-transparent",
|
|
174
176
|
solid: "bg-status-warning",
|
|
177
|
+
border: "border-status-warning",
|
|
175
178
|
icon: "text-status-warning",
|
|
176
179
|
text: "text-status-warning"
|
|
177
180
|
},
|
|
@@ -179,6 +182,7 @@ var statusColors = {
|
|
|
179
182
|
subtle: "bg-status-error-subtle",
|
|
180
183
|
outline: "border-2 border-status-error bg-transparent",
|
|
181
184
|
solid: "bg-status-error",
|
|
185
|
+
border: "border-status-error",
|
|
182
186
|
icon: "text-status-error",
|
|
183
187
|
text: "text-status-error"
|
|
184
188
|
}
|
|
@@ -192,6 +196,8 @@ var defaultIcons = {
|
|
|
192
196
|
function Alert({
|
|
193
197
|
status = "info",
|
|
194
198
|
variant = "subtle",
|
|
199
|
+
size = "default",
|
|
200
|
+
message,
|
|
195
201
|
icon,
|
|
196
202
|
showIcon = true,
|
|
197
203
|
onClose,
|
|
@@ -201,28 +207,43 @@ function Alert({
|
|
|
201
207
|
}) {
|
|
202
208
|
const colors = statusColors[status];
|
|
203
209
|
const isSolid = variant === "solid";
|
|
210
|
+
const isCompact = size === "compact";
|
|
204
211
|
return /* @__PURE__ */ jsxs(
|
|
205
212
|
reactNative.View,
|
|
206
213
|
{
|
|
207
214
|
accessibilityRole: "alert",
|
|
208
215
|
className: cn(
|
|
209
|
-
"flex-row items-start p-4 rounded-lg",
|
|
216
|
+
isCompact ? "flex-row items-center px-3 py-2 rounded-lg" : "flex-row items-start p-4 rounded-lg",
|
|
210
217
|
colors[variant],
|
|
218
|
+
// A compact `subtle` pill gets a hairline status border so it reads as a
|
|
219
|
+
// defined cue on a dark wall (the CueFlag look); other variants carry their own.
|
|
220
|
+
isCompact && variant === "subtle" && cn("border", colors.border),
|
|
211
221
|
className
|
|
212
222
|
),
|
|
213
223
|
...props,
|
|
214
224
|
children: [
|
|
215
|
-
showIcon && /* @__PURE__ */ jsx(reactNative.View, { className: "mr-3 mt-0.5", children: icon || /* @__PURE__ */ jsx(
|
|
225
|
+
showIcon && /* @__PURE__ */ jsx(reactNative.View, { className: isCompact ? "mr-2" : "mr-3 mt-0.5", children: icon || /* @__PURE__ */ jsx(
|
|
216
226
|
reactNative.Text,
|
|
217
227
|
{
|
|
218
228
|
className: cn(
|
|
219
|
-
"
|
|
229
|
+
"font-bold",
|
|
230
|
+
isCompact ? "text-base" : "text-lg",
|
|
220
231
|
isSolid ? "text-white" : colors.icon
|
|
221
232
|
),
|
|
222
233
|
children: defaultIcons[status]
|
|
223
234
|
}
|
|
224
235
|
) }),
|
|
225
|
-
/* @__PURE__ */
|
|
236
|
+
/* @__PURE__ */ jsxs(reactNative.View, { className: "flex-1", children: [
|
|
237
|
+
message != null && /* @__PURE__ */ jsx(
|
|
238
|
+
reactNative.Text,
|
|
239
|
+
{
|
|
240
|
+
className: cn("text-sm font-semibold", isSolid ? "text-white" : colors.text),
|
|
241
|
+
testID: "alert-message",
|
|
242
|
+
children: message
|
|
243
|
+
}
|
|
244
|
+
),
|
|
245
|
+
children
|
|
246
|
+
] }),
|
|
226
247
|
onClose && /* @__PURE__ */ jsx(
|
|
227
248
|
reactNative.Pressable,
|
|
228
249
|
{
|
|
@@ -5072,6 +5093,8 @@ function CircularProgress({
|
|
|
5072
5093
|
color = "primary",
|
|
5073
5094
|
showValue = false,
|
|
5074
5095
|
formatValue: formatValue3 = (v, m) => `${Math.round(v / m * 100)}%`,
|
|
5096
|
+
children,
|
|
5097
|
+
fill = false,
|
|
5075
5098
|
className,
|
|
5076
5099
|
...props
|
|
5077
5100
|
}) {
|
|
@@ -5080,11 +5103,12 @@ function CircularProgress({
|
|
|
5080
5103
|
const circumference = 2 * Math.PI * radius;
|
|
5081
5104
|
const strokeDashoffset = circumference - percentage / 100 * circumference;
|
|
5082
5105
|
const center = size / 2;
|
|
5106
|
+
const boxStyle = fill ? { width: "100%", aspectRatio: 1 } : { width: size, height: size };
|
|
5083
5107
|
return /* @__PURE__ */ jsxs(
|
|
5084
5108
|
reactNative.View,
|
|
5085
5109
|
{
|
|
5086
5110
|
className: cn("items-center justify-center", className),
|
|
5087
|
-
style:
|
|
5111
|
+
style: boxStyle,
|
|
5088
5112
|
accessibilityRole: "progressbar",
|
|
5089
5113
|
accessibilityValue: {
|
|
5090
5114
|
min: 0,
|
|
@@ -5093,52 +5117,56 @@ function CircularProgress({
|
|
|
5093
5117
|
},
|
|
5094
5118
|
...props,
|
|
5095
5119
|
children: [
|
|
5096
|
-
/* @__PURE__ */ jsx(
|
|
5120
|
+
/* @__PURE__ */ jsx(reactNative.View, { className: cn(isIndeterminate && "animate-spin"), style: boxStyle, children: /* @__PURE__ */ jsxs(
|
|
5121
|
+
"svg",
|
|
5122
|
+
{
|
|
5123
|
+
width: "100%",
|
|
5124
|
+
height: "100%",
|
|
5125
|
+
viewBox: `0 0 ${size} ${size}`,
|
|
5126
|
+
style: { position: "absolute" },
|
|
5127
|
+
children: [
|
|
5128
|
+
/* @__PURE__ */ jsx(
|
|
5129
|
+
"circle",
|
|
5130
|
+
{
|
|
5131
|
+
cx: center,
|
|
5132
|
+
cy: center,
|
|
5133
|
+
r: radius,
|
|
5134
|
+
fill: "none",
|
|
5135
|
+
stroke: "var(--color-border-default)",
|
|
5136
|
+
strokeWidth
|
|
5137
|
+
}
|
|
5138
|
+
),
|
|
5139
|
+
/* @__PURE__ */ jsx(
|
|
5140
|
+
"circle",
|
|
5141
|
+
{
|
|
5142
|
+
cx: center,
|
|
5143
|
+
cy: center,
|
|
5144
|
+
r: radius,
|
|
5145
|
+
fill: "none",
|
|
5146
|
+
stroke: colorVarMap[color],
|
|
5147
|
+
strokeWidth,
|
|
5148
|
+
strokeLinecap: "round",
|
|
5149
|
+
strokeDasharray: circumference,
|
|
5150
|
+
strokeDashoffset: isIndeterminate ? circumference * 0.75 : strokeDashoffset,
|
|
5151
|
+
transform: `rotate(-90 ${center} ${center})`,
|
|
5152
|
+
style: { transition: "stroke-dashoffset 300ms cubic-bezier(0.22, 1, 0.36, 1)" }
|
|
5153
|
+
}
|
|
5154
|
+
)
|
|
5155
|
+
]
|
|
5156
|
+
}
|
|
5157
|
+
) }),
|
|
5158
|
+
children != null ? /* @__PURE__ */ jsx(
|
|
5097
5159
|
reactNative.View,
|
|
5098
5160
|
{
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
"
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
viewBox: `0 0 ${size} ${size}`,
|
|
5107
|
-
style: { position: "absolute" },
|
|
5108
|
-
children: [
|
|
5109
|
-
/* @__PURE__ */ jsx(
|
|
5110
|
-
"circle",
|
|
5111
|
-
{
|
|
5112
|
-
cx: center,
|
|
5113
|
-
cy: center,
|
|
5114
|
-
r: radius,
|
|
5115
|
-
fill: "none",
|
|
5116
|
-
stroke: "var(--color-border-default)",
|
|
5117
|
-
strokeWidth
|
|
5118
|
-
}
|
|
5119
|
-
),
|
|
5120
|
-
/* @__PURE__ */ jsx(
|
|
5121
|
-
"circle",
|
|
5122
|
-
{
|
|
5123
|
-
cx: center,
|
|
5124
|
-
cy: center,
|
|
5125
|
-
r: radius,
|
|
5126
|
-
fill: "none",
|
|
5127
|
-
stroke: colorVarMap[color],
|
|
5128
|
-
strokeWidth,
|
|
5129
|
-
strokeLinecap: "round",
|
|
5130
|
-
strokeDasharray: circumference,
|
|
5131
|
-
strokeDashoffset: isIndeterminate ? circumference * 0.75 : strokeDashoffset,
|
|
5132
|
-
transform: `rotate(-90 ${center} ${center})`,
|
|
5133
|
-
style: { transition: "stroke-dashoffset 300ms cubic-bezier(0.22, 1, 0.36, 1)" }
|
|
5134
|
-
}
|
|
5135
|
-
)
|
|
5136
|
-
]
|
|
5137
|
-
}
|
|
5138
|
-
)
|
|
5161
|
+
style: {
|
|
5162
|
+
position: "absolute",
|
|
5163
|
+
alignItems: "center",
|
|
5164
|
+
justifyContent: "center",
|
|
5165
|
+
pointerEvents: "none"
|
|
5166
|
+
},
|
|
5167
|
+
children
|
|
5139
5168
|
}
|
|
5140
|
-
)
|
|
5141
|
-
showValue && !isIndeterminate && /* @__PURE__ */ jsx(
|
|
5169
|
+
) : showValue && !isIndeterminate && /* @__PURE__ */ jsx(
|
|
5142
5170
|
reactNative.Text,
|
|
5143
5171
|
{
|
|
5144
5172
|
className: "text-xs font-semibold text-text-primary absolute",
|
|
@@ -7079,6 +7107,70 @@ function TimerReadout({
|
|
|
7079
7107
|
}
|
|
7080
7108
|
);
|
|
7081
7109
|
}
|
|
7110
|
+
function CircularTimer({
|
|
7111
|
+
durationMs,
|
|
7112
|
+
elapsedMs,
|
|
7113
|
+
mode = "down",
|
|
7114
|
+
size = 160,
|
|
7115
|
+
strokeWidth = 8,
|
|
7116
|
+
fill = false,
|
|
7117
|
+
color = "primary",
|
|
7118
|
+
doneLabel,
|
|
7119
|
+
doneColor = "success",
|
|
7120
|
+
controls,
|
|
7121
|
+
accessibilityLabel: accessibilityLabel2,
|
|
7122
|
+
testID
|
|
7123
|
+
}) {
|
|
7124
|
+
const { remainingMs, progress, label, done } = useTimer({ mode, durationMs, elapsedMs });
|
|
7125
|
+
const isDone = done && mode === "down";
|
|
7126
|
+
const showDone = isDone && doneLabel != null;
|
|
7127
|
+
const remainingFrac = durationMs > 0 ? Math.max(0, Math.min(1, remainingMs / durationMs)) : 0;
|
|
7128
|
+
const arc = mode === "down" ? showDone ? 1 : remainingFrac : progress;
|
|
7129
|
+
const ringColor = showDone ? doneColor : color;
|
|
7130
|
+
const remainingSec = Math.ceil(Math.max(0, remainingMs) / 1e3);
|
|
7131
|
+
const a11y = accessibilityLabel2 ?? (isDone ? "Timer complete" : `${remainingSec} seconds remaining`);
|
|
7132
|
+
return /* @__PURE__ */ jsxs(
|
|
7133
|
+
reactNative.View,
|
|
7134
|
+
{
|
|
7135
|
+
style: { alignItems: "center", gap: 16 },
|
|
7136
|
+
accessibilityRole: "timer",
|
|
7137
|
+
accessibilityLabel: a11y,
|
|
7138
|
+
testID: testID ?? "circular-timer",
|
|
7139
|
+
children: [
|
|
7140
|
+
/* @__PURE__ */ jsx(
|
|
7141
|
+
CircularProgress,
|
|
7142
|
+
{
|
|
7143
|
+
value: arc,
|
|
7144
|
+
max: 1,
|
|
7145
|
+
size,
|
|
7146
|
+
strokeWidth,
|
|
7147
|
+
fill,
|
|
7148
|
+
color: ringColor,
|
|
7149
|
+
accessibilityLabel: "Timer ring",
|
|
7150
|
+
testID: "circular-timer-ring",
|
|
7151
|
+
children: /* @__PURE__ */ jsx(
|
|
7152
|
+
reactNative.Text,
|
|
7153
|
+
{
|
|
7154
|
+
className: showDone ? void 0 : "text-text-primary",
|
|
7155
|
+
style: {
|
|
7156
|
+
fontSize: Math.round(size * (showDone ? 0.28 : 0.24)),
|
|
7157
|
+
fontFamily: '"Space Grotesk", sans-serif',
|
|
7158
|
+
fontWeight: "800",
|
|
7159
|
+
fontVariant: ["tabular-nums"],
|
|
7160
|
+
letterSpacing: showDone ? 1 : -1,
|
|
7161
|
+
...showDone ? { color: colorVarMap[doneColor] } : null
|
|
7162
|
+
},
|
|
7163
|
+
testID: "circular-timer-label",
|
|
7164
|
+
children: showDone ? doneLabel : label
|
|
7165
|
+
}
|
|
7166
|
+
)
|
|
7167
|
+
}
|
|
7168
|
+
),
|
|
7169
|
+
controls
|
|
7170
|
+
]
|
|
7171
|
+
}
|
|
7172
|
+
);
|
|
7173
|
+
}
|
|
7082
7174
|
var sizeConfig2 = {
|
|
7083
7175
|
sm: { value: "text-lg font-bold", label: "text-xs", unit: "text-xs" },
|
|
7084
7176
|
md: { value: "text-2xl font-bold", label: "text-xs", unit: "text-sm" },
|
|
@@ -7729,6 +7821,11 @@ var TEMPO_PACING = {
|
|
|
7729
7821
|
behindThresholdPct: 0.15,
|
|
7730
7822
|
minPhaseDurationMs: 500
|
|
7731
7823
|
};
|
|
7824
|
+
function formatTempoDelta(remainingMs) {
|
|
7825
|
+
const s = remainingMs / 1e3;
|
|
7826
|
+
const rounded = Math.abs(s) < 0.05 ? 0 : s;
|
|
7827
|
+
return rounded.toFixed(1);
|
|
7828
|
+
}
|
|
7732
7829
|
function getTempoPacingState(elapsedMs, targetMs) {
|
|
7733
7830
|
if (!targetMs || targetMs < TEMPO_PACING.minPhaseDurationMs) return "none";
|
|
7734
7831
|
const ratio = elapsedMs / targetMs;
|
|
@@ -7741,24 +7838,33 @@ function getTempoFillPct(elapsedMs, targetMs) {
|
|
|
7741
7838
|
}
|
|
7742
7839
|
var PHASE_ORDER = ["concentric", "hold", "eccentric"];
|
|
7743
7840
|
var PHASE_CONFIG = {
|
|
7744
|
-
concentric: { label: "Con", color: t2["status-success"], flex: 2 },
|
|
7745
|
-
hold: { label: "Hold", color: t2["brand-primary"], flex: 1 },
|
|
7746
|
-
eccentric: { label: "Ecc", color: t2["status-warning"], flex: 3 }
|
|
7841
|
+
concentric: { label: "Con", wallLabel: "Concentric", color: t2["status-success"], flex: 2 },
|
|
7842
|
+
hold: { label: "Hold", wallLabel: "Hold", color: t2["brand-primary"], flex: 1 },
|
|
7843
|
+
eccentric: { label: "Ecc", wallLabel: "Eccentric", color: t2["status-warning"], flex: 3 }
|
|
7844
|
+
};
|
|
7845
|
+
var TEMPO_SIZES = {
|
|
7846
|
+
default: { labelMargin: 4, labelFont: 10, barGap: 2, barHeight: 20, radius: 4, segFont: 12 },
|
|
7847
|
+
wall: { labelMargin: 8, labelFont: 15, barGap: 4, barHeight: 40, radius: 6, segFont: 22 }
|
|
7747
7848
|
};
|
|
7748
7849
|
function TempoBar({
|
|
7749
7850
|
activePhase,
|
|
7750
7851
|
phaseElapsedMs,
|
|
7751
7852
|
completed,
|
|
7752
7853
|
target,
|
|
7854
|
+
size = "default",
|
|
7855
|
+
activeDisplay = "time",
|
|
7753
7856
|
className,
|
|
7754
7857
|
...props
|
|
7755
7858
|
}) {
|
|
7859
|
+
const s = TEMPO_SIZES[size];
|
|
7860
|
+
const [display, setDisplay] = React24.useState(activeDisplay);
|
|
7861
|
+
const toggleDisplay = () => setDisplay((d) => d === "time" ? "delta" : "time");
|
|
7756
7862
|
return /* @__PURE__ */ jsxs(reactNative.View, { className, testID: "tempo-bar", ...props, children: [
|
|
7757
|
-
/* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", marginBottom:
|
|
7863
|
+
/* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", marginBottom: s.labelMargin }, children: PHASE_ORDER.map((phase) => {
|
|
7758
7864
|
const config = PHASE_CONFIG[phase];
|
|
7759
|
-
return /* @__PURE__ */ jsx(reactNative.View, { style: { flex: config.flex, alignItems: "center" }, children: /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize:
|
|
7865
|
+
return /* @__PURE__ */ jsx(reactNative.View, { style: { flex: config.flex, alignItems: "center" }, children: /* @__PURE__ */ jsx(reactNative.Text, { numberOfLines: 1, style: { fontSize: s.labelFont, color: t2["text-disabled"] }, children: size === "wall" ? config.wallLabel : config.label }) }, phase);
|
|
7760
7866
|
}) }),
|
|
7761
|
-
/* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", gap:
|
|
7867
|
+
/* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", gap: s.barGap, height: s.barHeight }, children: PHASE_ORDER.map((phase) => {
|
|
7762
7868
|
const config = PHASE_CONFIG[phase];
|
|
7763
7869
|
const isActive = activePhase === phase;
|
|
7764
7870
|
const completedMs = completed?.[phase];
|
|
@@ -7770,7 +7876,7 @@ function TempoBar({
|
|
|
7770
7876
|
style: {
|
|
7771
7877
|
flex: config.flex,
|
|
7772
7878
|
backgroundColor: alpha("#ffffff", 0.06),
|
|
7773
|
-
borderRadius:
|
|
7879
|
+
borderRadius: s.radius,
|
|
7774
7880
|
overflow: "hidden"
|
|
7775
7881
|
},
|
|
7776
7882
|
children: isActive ? /* @__PURE__ */ jsx(
|
|
@@ -7778,9 +7884,21 @@ function TempoBar({
|
|
|
7778
7884
|
{
|
|
7779
7885
|
config,
|
|
7780
7886
|
phaseElapsedMs,
|
|
7781
|
-
targetMs
|
|
7887
|
+
targetMs,
|
|
7888
|
+
radius: s.radius,
|
|
7889
|
+
font: s.segFont,
|
|
7890
|
+
display,
|
|
7891
|
+
onToggleDisplay: toggleDisplay
|
|
7782
7892
|
}
|
|
7783
|
-
) : completedMs != null ? /* @__PURE__ */ jsx(
|
|
7893
|
+
) : completedMs != null ? /* @__PURE__ */ jsx(
|
|
7894
|
+
CompletedSegment,
|
|
7895
|
+
{
|
|
7896
|
+
config,
|
|
7897
|
+
completedMs,
|
|
7898
|
+
targetMs,
|
|
7899
|
+
font: s.segFont
|
|
7900
|
+
}
|
|
7901
|
+
) : null
|
|
7784
7902
|
},
|
|
7785
7903
|
phase
|
|
7786
7904
|
);
|
|
@@ -7790,49 +7908,65 @@ function TempoBar({
|
|
|
7790
7908
|
function ActiveSegment({
|
|
7791
7909
|
config,
|
|
7792
7910
|
phaseElapsedMs,
|
|
7793
|
-
targetMs
|
|
7911
|
+
targetMs,
|
|
7912
|
+
radius,
|
|
7913
|
+
font,
|
|
7914
|
+
display,
|
|
7915
|
+
onToggleDisplay
|
|
7794
7916
|
}) {
|
|
7795
7917
|
const pacing = getTempoPacingState(phaseElapsedMs, targetMs);
|
|
7796
7918
|
const barColor = pacing === "behind" ? t2["status-error"] : config.color;
|
|
7797
7919
|
const fillPct = getTempoFillPct(phaseElapsedMs, targetMs);
|
|
7798
|
-
const label = targetMs ? `${formatDuration2(phaseElapsedMs)} / ${formatDuration2(targetMs)}` : formatDuration2(phaseElapsedMs);
|
|
7799
|
-
return /* @__PURE__ */ jsxs(
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7920
|
+
const label = display === "delta" && targetMs != null ? formatTempoDelta(targetMs - phaseElapsedMs) : targetMs != null ? `${formatDuration2(phaseElapsedMs)} / ${formatDuration2(targetMs)}` : formatDuration2(phaseElapsedMs);
|
|
7921
|
+
return /* @__PURE__ */ jsxs(
|
|
7922
|
+
reactNative.Pressable,
|
|
7923
|
+
{
|
|
7924
|
+
onPress: onToggleDisplay,
|
|
7925
|
+
style: { height: "100%", position: "relative" },
|
|
7926
|
+
accessibilityRole: "button",
|
|
7927
|
+
accessibilityLabel: `Tempo readout: ${display === "delta" ? "delta" : "time"} \u2014 tap to switch`,
|
|
7928
|
+
testID: `tempo-segment-active-${config.label}`,
|
|
7929
|
+
children: [
|
|
7930
|
+
/* @__PURE__ */ jsx(
|
|
7931
|
+
reactNative.View,
|
|
7932
|
+
{
|
|
7933
|
+
style: {
|
|
7934
|
+
position: "absolute",
|
|
7935
|
+
left: 0,
|
|
7936
|
+
top: 0,
|
|
7937
|
+
bottom: 0,
|
|
7938
|
+
width: `${fillPct}%`,
|
|
7939
|
+
backgroundColor: alpha(barColor, 0.3),
|
|
7940
|
+
borderRadius: radius
|
|
7941
|
+
}
|
|
7942
|
+
}
|
|
7943
|
+
),
|
|
7944
|
+
/* @__PURE__ */ jsx(
|
|
7945
|
+
reactNative.View,
|
|
7946
|
+
{
|
|
7947
|
+
style: {
|
|
7948
|
+
height: "100%",
|
|
7949
|
+
flexDirection: "row",
|
|
7950
|
+
alignItems: "center",
|
|
7951
|
+
justifyContent: "center"
|
|
7952
|
+
},
|
|
7953
|
+
children: /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: font, fontWeight: "700", color: barColor }, children: label })
|
|
7954
|
+
}
|
|
7955
|
+
)
|
|
7956
|
+
]
|
|
7957
|
+
}
|
|
7958
|
+
);
|
|
7827
7959
|
}
|
|
7828
7960
|
function CompletedSegment({
|
|
7829
7961
|
config,
|
|
7830
7962
|
completedMs,
|
|
7831
|
-
targetMs
|
|
7963
|
+
targetMs,
|
|
7964
|
+
font
|
|
7832
7965
|
}) {
|
|
7833
7966
|
const pacing = getTempoPacingState(completedMs, targetMs);
|
|
7834
7967
|
const hitTarget = pacing !== "behind";
|
|
7835
7968
|
const indicator = targetMs && targetMs >= TEMPO_PACING.minPhaseDurationMs ? hitTarget ? " \u2713" : " \u2717" : "";
|
|
7969
|
+
const cueColor = hitTarget ? config.color : t2["status-error"];
|
|
7836
7970
|
return /* @__PURE__ */ jsx(
|
|
7837
7971
|
reactNative.View,
|
|
7838
7972
|
{
|
|
@@ -7841,13 +7975,23 @@ function CompletedSegment({
|
|
|
7841
7975
|
flexDirection: "row",
|
|
7842
7976
|
alignItems: "center",
|
|
7843
7977
|
justifyContent: "center",
|
|
7844
|
-
backgroundColor: alpha(
|
|
7978
|
+
backgroundColor: alpha(cueColor, 0.15)
|
|
7845
7979
|
},
|
|
7846
7980
|
testID: `tempo-segment-completed-${config.label}`,
|
|
7847
|
-
children: /* @__PURE__ */ jsxs(
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7981
|
+
children: /* @__PURE__ */ jsxs(
|
|
7982
|
+
reactNative.Text,
|
|
7983
|
+
{
|
|
7984
|
+
style: {
|
|
7985
|
+
fontSize: font,
|
|
7986
|
+
fontWeight: "500",
|
|
7987
|
+
color: hitTarget ? alpha(config.color, 0.7) : cueColor
|
|
7988
|
+
},
|
|
7989
|
+
children: [
|
|
7990
|
+
formatDuration2(completedMs),
|
|
7991
|
+
indicator
|
|
7992
|
+
]
|
|
7993
|
+
}
|
|
7994
|
+
)
|
|
7851
7995
|
}
|
|
7852
7996
|
);
|
|
7853
7997
|
}
|
|
@@ -8937,6 +9081,14 @@ var EXPANDED_ENCODED_PCT = 45;
|
|
|
8937
9081
|
var EXPANDED_HEIGHT = 60;
|
|
8938
9082
|
var FIXED_MAX_VELOCITY = 1.15;
|
|
8939
9083
|
var BARE_STUB_HEIGHT = 3;
|
|
9084
|
+
var HERO_HEIGHT = 220;
|
|
9085
|
+
var HERO_LABEL_HEADROOM = 20;
|
|
9086
|
+
var HERO_BAR_GAP = 8;
|
|
9087
|
+
var HERO_BAR_MAX_WIDTH = 52;
|
|
9088
|
+
var HERO_BAR_RADIUS = 5;
|
|
9089
|
+
var HERO_MIN_BAR_HEIGHT = 4;
|
|
9090
|
+
var HERO_PENDING_COLOR = primitiveColors.charcoal[100];
|
|
9091
|
+
var HERO_REFERENCE_COLOR = primitiveColors.charcoal[0];
|
|
8940
9092
|
function scaleDenominator(scale, maxVelocity) {
|
|
8941
9093
|
return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * 1.15;
|
|
8942
9094
|
}
|
|
@@ -8964,6 +9116,183 @@ function usePrefersReducedMotion() {
|
|
|
8964
9116
|
var ANIMATION_DURATION = 400;
|
|
8965
9117
|
var ANIMATION_EASING = reactNative.Easing.bezier(0.22, 1, 0.36, 1);
|
|
8966
9118
|
var POP_EASING = reactNative.Easing.bezier(0.34, 1.56, 0.64, 1);
|
|
9119
|
+
function useLiveRepPop(active, liveRepIndex, liveVelocity, isNewPeak) {
|
|
9120
|
+
const prefersReducedMotion = usePrefersReducedMotion();
|
|
9121
|
+
const [liveScale] = React24.useState(() => new reactNative.Animated.Value(1));
|
|
9122
|
+
React24.useEffect(() => {
|
|
9123
|
+
if (!active || liveRepIndex == null || liveVelocity == null) return;
|
|
9124
|
+
if (prefersReducedMotion) {
|
|
9125
|
+
liveScale.setValue(1);
|
|
9126
|
+
return;
|
|
9127
|
+
}
|
|
9128
|
+
if (isNewPeak) {
|
|
9129
|
+
liveScale.setValue(1);
|
|
9130
|
+
reactNative.Animated.sequence([
|
|
9131
|
+
reactNative.Animated.timing(liveScale, {
|
|
9132
|
+
toValue: 1.25,
|
|
9133
|
+
duration: 150,
|
|
9134
|
+
easing: reactNative.Easing.out(reactNative.Easing.quad),
|
|
9135
|
+
useNativeDriver: true
|
|
9136
|
+
}),
|
|
9137
|
+
reactNative.Animated.spring(liveScale, {
|
|
9138
|
+
toValue: 1,
|
|
9139
|
+
friction: 3,
|
|
9140
|
+
tension: 140,
|
|
9141
|
+
useNativeDriver: true
|
|
9142
|
+
})
|
|
9143
|
+
]).start();
|
|
9144
|
+
} else {
|
|
9145
|
+
liveScale.setValue(0.8);
|
|
9146
|
+
reactNative.Animated.timing(liveScale, {
|
|
9147
|
+
toValue: 1,
|
|
9148
|
+
duration: 300,
|
|
9149
|
+
easing: POP_EASING,
|
|
9150
|
+
useNativeDriver: true
|
|
9151
|
+
}).start();
|
|
9152
|
+
}
|
|
9153
|
+
}, [active, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
|
|
9154
|
+
return liveScale;
|
|
9155
|
+
}
|
|
9156
|
+
function HeroVelocityChart({
|
|
9157
|
+
doneVelocities,
|
|
9158
|
+
barColorFor,
|
|
9159
|
+
scaleDenom,
|
|
9160
|
+
referenceVelocity,
|
|
9161
|
+
liveRepIndex,
|
|
9162
|
+
isNewPeak,
|
|
9163
|
+
targetReps,
|
|
9164
|
+
height,
|
|
9165
|
+
className,
|
|
9166
|
+
viewProps
|
|
9167
|
+
}) {
|
|
9168
|
+
const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
|
|
9169
|
+
const liveScale = useLiveRepPop(true, liveRepIndex, liveVelocity, isNewPeak);
|
|
9170
|
+
const plotHeight = Math.max(0, height - HERO_LABEL_HEADROOM);
|
|
9171
|
+
const barHeight = (velocity) => scaleDenom > 0 ? Math.max(HERO_MIN_BAR_HEIGHT, Math.min(1, velocity / scaleDenom) * plotHeight) : HERO_MIN_BAR_HEIGHT;
|
|
9172
|
+
const pendingCount = Math.max(0, (targetReps ?? 0) - doneVelocities.length);
|
|
9173
|
+
const referencePx = referenceVelocity > 0 && scaleDenom > 0 ? Math.min(plotHeight, referenceVelocity / scaleDenom * plotHeight) : 0;
|
|
9174
|
+
const repCount = doneVelocities.length;
|
|
9175
|
+
const total = Math.max(repCount, targetReps ?? repCount);
|
|
9176
|
+
const label = referenceVelocity > 0 ? `Velocity chart, ${repCount} of ${total} reps, best ${formatVelocity(referenceVelocity)} meters per second` : `Velocity chart, ${repCount} of ${total} reps`;
|
|
9177
|
+
const { style: externalStyle, ...restProps } = viewProps;
|
|
9178
|
+
return /* @__PURE__ */ jsx(
|
|
9179
|
+
reactNative.View,
|
|
9180
|
+
{
|
|
9181
|
+
className,
|
|
9182
|
+
style: [{ height }, externalStyle],
|
|
9183
|
+
accessibilityRole: "image",
|
|
9184
|
+
accessibilityLabel: label,
|
|
9185
|
+
testID: "velocity-strip-hero",
|
|
9186
|
+
...restProps,
|
|
9187
|
+
children: /* @__PURE__ */ jsxs(
|
|
9188
|
+
reactNative.View,
|
|
9189
|
+
{
|
|
9190
|
+
style: {
|
|
9191
|
+
flex: 1,
|
|
9192
|
+
flexDirection: "row",
|
|
9193
|
+
alignItems: "flex-end",
|
|
9194
|
+
gap: HERO_BAR_GAP,
|
|
9195
|
+
position: "relative",
|
|
9196
|
+
borderBottomWidth: 2,
|
|
9197
|
+
borderBottomColor: HERO_PENDING_COLOR
|
|
9198
|
+
},
|
|
9199
|
+
children: [
|
|
9200
|
+
referencePx > 0 && /* @__PURE__ */ jsx(
|
|
9201
|
+
reactNative.View,
|
|
9202
|
+
{
|
|
9203
|
+
accessibilityElementsHidden: true,
|
|
9204
|
+
style: {
|
|
9205
|
+
position: "absolute",
|
|
9206
|
+
left: 0,
|
|
9207
|
+
right: 0,
|
|
9208
|
+
bottom: referencePx,
|
|
9209
|
+
borderTopWidth: 1,
|
|
9210
|
+
borderStyle: "dashed",
|
|
9211
|
+
borderColor: HERO_REFERENCE_COLOR,
|
|
9212
|
+
pointerEvents: "none"
|
|
9213
|
+
},
|
|
9214
|
+
testID: "velocity-hero-reference"
|
|
9215
|
+
}
|
|
9216
|
+
),
|
|
9217
|
+
doneVelocities.map((velocity, i) => {
|
|
9218
|
+
const isLive = liveRepIndex === i;
|
|
9219
|
+
return /* @__PURE__ */ jsxs(
|
|
9220
|
+
reactNative.View,
|
|
9221
|
+
{
|
|
9222
|
+
accessibilityElementsHidden: true,
|
|
9223
|
+
style: {
|
|
9224
|
+
flex: 1,
|
|
9225
|
+
maxWidth: HERO_BAR_MAX_WIDTH,
|
|
9226
|
+
height: "100%",
|
|
9227
|
+
alignItems: "center",
|
|
9228
|
+
justifyContent: "flex-end"
|
|
9229
|
+
},
|
|
9230
|
+
children: [
|
|
9231
|
+
/* @__PURE__ */ jsx(
|
|
9232
|
+
reactNative.Text,
|
|
9233
|
+
{
|
|
9234
|
+
className: "text-text-primary",
|
|
9235
|
+
style: { fontSize: 12, fontWeight: "800", marginBottom: 4 },
|
|
9236
|
+
testID: `velocity-label-${i}`,
|
|
9237
|
+
children: formatVelocity(velocity)
|
|
9238
|
+
}
|
|
9239
|
+
),
|
|
9240
|
+
/* @__PURE__ */ jsx(
|
|
9241
|
+
reactNative.Animated.View,
|
|
9242
|
+
{
|
|
9243
|
+
style: [
|
|
9244
|
+
{
|
|
9245
|
+
width: "100%",
|
|
9246
|
+
height: barHeight(velocity),
|
|
9247
|
+
borderTopLeftRadius: HERO_BAR_RADIUS,
|
|
9248
|
+
borderTopRightRadius: HERO_BAR_RADIUS,
|
|
9249
|
+
backgroundColor: barColorFor(velocity)
|
|
9250
|
+
},
|
|
9251
|
+
isLive ? { transform: [{ scale: liveScale }] } : null
|
|
9252
|
+
],
|
|
9253
|
+
testID: `velocity-bar-${i}`
|
|
9254
|
+
}
|
|
9255
|
+
)
|
|
9256
|
+
]
|
|
9257
|
+
},
|
|
9258
|
+
i
|
|
9259
|
+
);
|
|
9260
|
+
}),
|
|
9261
|
+
Array.from({ length: pendingCount }, (_, i) => /* @__PURE__ */ jsx(
|
|
9262
|
+
reactNative.View,
|
|
9263
|
+
{
|
|
9264
|
+
accessibilityElementsHidden: true,
|
|
9265
|
+
style: {
|
|
9266
|
+
flex: 1,
|
|
9267
|
+
maxWidth: HERO_BAR_MAX_WIDTH,
|
|
9268
|
+
height: "100%",
|
|
9269
|
+
alignItems: "center",
|
|
9270
|
+
justifyContent: "flex-end"
|
|
9271
|
+
},
|
|
9272
|
+
children: /* @__PURE__ */ jsx(
|
|
9273
|
+
reactNative.View,
|
|
9274
|
+
{
|
|
9275
|
+
style: {
|
|
9276
|
+
width: "100%",
|
|
9277
|
+
height: HERO_MIN_BAR_HEIGHT * 3,
|
|
9278
|
+
borderWidth: 1,
|
|
9279
|
+
borderStyle: "dashed",
|
|
9280
|
+
borderColor: HERO_PENDING_COLOR,
|
|
9281
|
+
borderTopLeftRadius: HERO_BAR_RADIUS,
|
|
9282
|
+
borderTopRightRadius: HERO_BAR_RADIUS
|
|
9283
|
+
},
|
|
9284
|
+
testID: "velocity-slot-todo"
|
|
9285
|
+
}
|
|
9286
|
+
)
|
|
9287
|
+
},
|
|
9288
|
+
`pending-${i}`
|
|
9289
|
+
))
|
|
9290
|
+
]
|
|
9291
|
+
}
|
|
9292
|
+
)
|
|
9293
|
+
}
|
|
9294
|
+
);
|
|
9295
|
+
}
|
|
8967
9296
|
function VelocityStrip({
|
|
8968
9297
|
velocities,
|
|
8969
9298
|
set,
|
|
@@ -8974,6 +9303,7 @@ function VelocityStrip({
|
|
|
8974
9303
|
onRepPress,
|
|
8975
9304
|
variant = "expanded",
|
|
8976
9305
|
height = EXPANDED_HEIGHT,
|
|
9306
|
+
targetReps,
|
|
8977
9307
|
scale = "peak",
|
|
8978
9308
|
showNumbers = true,
|
|
8979
9309
|
showInfo = true,
|
|
@@ -8999,45 +9329,17 @@ function VelocityStrip({
|
|
|
8999
9329
|
return SET_STRIP_VARIABLE_COLOR;
|
|
9000
9330
|
};
|
|
9001
9331
|
const meanZone = hasZones ? classifyBand(meanVelocity, zones)?.label ?? "" : getVelocityZoneName(meanVelocity);
|
|
9002
|
-
const prefersReducedMotion = usePrefersReducedMotion();
|
|
9003
9332
|
const [heightAnim] = React24.useState(() => new reactNative.Animated.Value(expanded ? height : 3));
|
|
9004
9333
|
const [labelOpacity] = React24.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
|
|
9005
9334
|
const [infoOpacity] = React24.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
|
|
9006
|
-
const [liveScale] = React24.useState(() => new reactNative.Animated.Value(1));
|
|
9007
9335
|
const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
|
|
9008
9336
|
const isNewPeak = liveVelocity != null && maxVelocity > 0 && liveVelocity === maxVelocity;
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
if (isNewPeak) {
|
|
9016
|
-
liveScale.setValue(1);
|
|
9017
|
-
reactNative.Animated.sequence([
|
|
9018
|
-
reactNative.Animated.timing(liveScale, {
|
|
9019
|
-
toValue: 1.25,
|
|
9020
|
-
duration: 150,
|
|
9021
|
-
easing: reactNative.Easing.out(reactNative.Easing.quad),
|
|
9022
|
-
useNativeDriver: true
|
|
9023
|
-
}),
|
|
9024
|
-
reactNative.Animated.spring(liveScale, {
|
|
9025
|
-
toValue: 1,
|
|
9026
|
-
friction: 3,
|
|
9027
|
-
tension: 140,
|
|
9028
|
-
useNativeDriver: true
|
|
9029
|
-
})
|
|
9030
|
-
]).start();
|
|
9031
|
-
} else {
|
|
9032
|
-
liveScale.setValue(0.8);
|
|
9033
|
-
reactNative.Animated.timing(liveScale, {
|
|
9034
|
-
toValue: 1,
|
|
9035
|
-
duration: 300,
|
|
9036
|
-
easing: POP_EASING,
|
|
9037
|
-
useNativeDriver: true
|
|
9038
|
-
}).start();
|
|
9039
|
-
}
|
|
9040
|
-
}, [variant, framed, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
|
|
9337
|
+
const liveScale = useLiveRepPop(
|
|
9338
|
+
variant === "expanded" && framed,
|
|
9339
|
+
liveRepIndex,
|
|
9340
|
+
liveVelocity,
|
|
9341
|
+
isNewPeak
|
|
9342
|
+
);
|
|
9041
9343
|
React24.useEffect(() => {
|
|
9042
9344
|
if (variant !== "expanded" || !framed) return;
|
|
9043
9345
|
reactNative.Animated.parallel([
|
|
@@ -9064,6 +9366,24 @@ function VelocityStrip({
|
|
|
9064
9366
|
if (set == null && velocities == null) return null;
|
|
9065
9367
|
const repCount = doneVelocities.length;
|
|
9066
9368
|
const miniLabel = set ? setAccessibilityLabel(set, repCount) : `Velocity strip, ${repCount} reps`;
|
|
9369
|
+
if (variant === "hero") {
|
|
9370
|
+
const heroHeight = height === EXPANDED_HEIGHT ? HERO_HEIGHT : height;
|
|
9371
|
+
return /* @__PURE__ */ jsx(
|
|
9372
|
+
HeroVelocityChart,
|
|
9373
|
+
{
|
|
9374
|
+
doneVelocities,
|
|
9375
|
+
barColorFor,
|
|
9376
|
+
scaleDenom,
|
|
9377
|
+
referenceVelocity: maxVelocity,
|
|
9378
|
+
liveRepIndex,
|
|
9379
|
+
isNewPeak,
|
|
9380
|
+
targetReps,
|
|
9381
|
+
height: heroHeight,
|
|
9382
|
+
className,
|
|
9383
|
+
viewProps: props
|
|
9384
|
+
}
|
|
9385
|
+
);
|
|
9386
|
+
}
|
|
9067
9387
|
if (variant === "mini") {
|
|
9068
9388
|
const { style: externalStyle, ...restProps } = props;
|
|
9069
9389
|
return /* @__PURE__ */ jsx(
|
|
@@ -9828,6 +10148,99 @@ function InputBar({
|
|
|
9828
10148
|
);
|
|
9829
10149
|
}
|
|
9830
10150
|
var BRAND_PRIMARY4 = getSemanticColors("dark")["brand-primary"];
|
|
10151
|
+
var RING_DEFAULT_SIZE = 180;
|
|
10152
|
+
function RestActions({ onAddTime, onSkip }) {
|
|
10153
|
+
return /* @__PURE__ */ jsxs(reactNative.View, { style: { flexDirection: "row", gap: 8 }, children: [
|
|
10154
|
+
/* @__PURE__ */ jsx(
|
|
10155
|
+
reactNative.Pressable,
|
|
10156
|
+
{
|
|
10157
|
+
onPress: onAddTime,
|
|
10158
|
+
style: {
|
|
10159
|
+
backgroundColor: "rgba(255,255,255,0.06)",
|
|
10160
|
+
paddingVertical: 8,
|
|
10161
|
+
paddingHorizontal: 20,
|
|
10162
|
+
borderRadius: 8
|
|
10163
|
+
},
|
|
10164
|
+
accessibilityRole: "button",
|
|
10165
|
+
accessibilityLabel: "Add 30 seconds",
|
|
10166
|
+
testID: "rest-timer-add-time",
|
|
10167
|
+
children: /* @__PURE__ */ jsx(
|
|
10168
|
+
reactNative.Text,
|
|
10169
|
+
{
|
|
10170
|
+
className: "text-text-secondary",
|
|
10171
|
+
style: { fontSize: 11, fontFamily: "Inter, sans-serif", fontWeight: "600" },
|
|
10172
|
+
children: "+30s"
|
|
10173
|
+
}
|
|
10174
|
+
)
|
|
10175
|
+
}
|
|
10176
|
+
),
|
|
10177
|
+
/* @__PURE__ */ jsx(
|
|
10178
|
+
reactNative.Pressable,
|
|
10179
|
+
{
|
|
10180
|
+
onPress: onSkip,
|
|
10181
|
+
style: {
|
|
10182
|
+
backgroundColor: "rgba(255,121,0,0.12)",
|
|
10183
|
+
paddingVertical: 8,
|
|
10184
|
+
paddingHorizontal: 20,
|
|
10185
|
+
borderRadius: 8
|
|
10186
|
+
},
|
|
10187
|
+
accessibilityRole: "button",
|
|
10188
|
+
accessibilityLabel: "Skip rest",
|
|
10189
|
+
testID: "rest-timer-skip",
|
|
10190
|
+
children: /* @__PURE__ */ jsx(
|
|
10191
|
+
reactNative.Text,
|
|
10192
|
+
{
|
|
10193
|
+
style: {
|
|
10194
|
+
fontSize: 11,
|
|
10195
|
+
fontFamily: "Inter, sans-serif",
|
|
10196
|
+
fontWeight: "600",
|
|
10197
|
+
color: BRAND_PRIMARY4
|
|
10198
|
+
},
|
|
10199
|
+
children: "Skip"
|
|
10200
|
+
}
|
|
10201
|
+
)
|
|
10202
|
+
}
|
|
10203
|
+
)
|
|
10204
|
+
] });
|
|
10205
|
+
}
|
|
10206
|
+
function RestTimerRing({
|
|
10207
|
+
totalSeconds,
|
|
10208
|
+
elapsedMs,
|
|
10209
|
+
remainingSec,
|
|
10210
|
+
done,
|
|
10211
|
+
size,
|
|
10212
|
+
nextSetInfo,
|
|
10213
|
+
displayOnly,
|
|
10214
|
+
onSkip,
|
|
10215
|
+
onAddTime
|
|
10216
|
+
}) {
|
|
10217
|
+
const a11yLabel = done ? "Rest complete, next set ready" : `Rest timer, ${Math.max(0, remainingSec)} seconds remaining`;
|
|
10218
|
+
return /* @__PURE__ */ jsxs(reactNative.View, { style: { alignItems: "center", gap: 16 }, testID: "rest-timer", children: [
|
|
10219
|
+
/* @__PURE__ */ jsx(
|
|
10220
|
+
CircularTimer,
|
|
10221
|
+
{
|
|
10222
|
+
durationMs: totalSeconds * 1e3,
|
|
10223
|
+
elapsedMs,
|
|
10224
|
+
mode: "down",
|
|
10225
|
+
size,
|
|
10226
|
+
doneLabel: "GO",
|
|
10227
|
+
doneColor: "success",
|
|
10228
|
+
accessibilityLabel: a11yLabel,
|
|
10229
|
+
controls: !displayOnly ? /* @__PURE__ */ jsx(RestActions, { onAddTime, onSkip }) : void 0,
|
|
10230
|
+
testID: "rest-timer-ring"
|
|
10231
|
+
}
|
|
10232
|
+
),
|
|
10233
|
+
nextSetInfo != null && /* @__PURE__ */ jsx(
|
|
10234
|
+
reactNative.Text,
|
|
10235
|
+
{
|
|
10236
|
+
className: "text-text-tertiary",
|
|
10237
|
+
style: { fontSize: 13, fontFamily: "Inter, sans-serif" },
|
|
10238
|
+
testID: "rest-timer-next-set",
|
|
10239
|
+
children: nextSetInfo
|
|
10240
|
+
}
|
|
10241
|
+
)
|
|
10242
|
+
] });
|
|
10243
|
+
}
|
|
9831
10244
|
function RestTimer({
|
|
9832
10245
|
totalSeconds,
|
|
9833
10246
|
elapsedMs,
|
|
@@ -9835,12 +10248,15 @@ function RestTimer({
|
|
|
9835
10248
|
onAddTime,
|
|
9836
10249
|
nextSetInfo,
|
|
9837
10250
|
visible,
|
|
9838
|
-
displayOnly = false
|
|
10251
|
+
displayOnly = false,
|
|
10252
|
+
variant = "bar",
|
|
10253
|
+
size = RING_DEFAULT_SIZE
|
|
9839
10254
|
}) {
|
|
9840
10255
|
const {
|
|
9841
10256
|
remainingMs,
|
|
9842
10257
|
progress,
|
|
9843
|
-
label: timeDisplay
|
|
10258
|
+
label: timeDisplay,
|
|
10259
|
+
done
|
|
9844
10260
|
} = useTimer({
|
|
9845
10261
|
mode: "down",
|
|
9846
10262
|
durationMs: totalSeconds * 1e3,
|
|
@@ -9849,6 +10265,22 @@ function RestTimer({
|
|
|
9849
10265
|
const remainingSec = Math.ceil(remainingMs / 1e3);
|
|
9850
10266
|
const progressPct = progress * 100;
|
|
9851
10267
|
if (!visible) return null;
|
|
10268
|
+
if (variant === "ring") {
|
|
10269
|
+
return /* @__PURE__ */ jsx(
|
|
10270
|
+
RestTimerRing,
|
|
10271
|
+
{
|
|
10272
|
+
totalSeconds,
|
|
10273
|
+
elapsedMs,
|
|
10274
|
+
remainingSec,
|
|
10275
|
+
done,
|
|
10276
|
+
size,
|
|
10277
|
+
nextSetInfo,
|
|
10278
|
+
displayOnly,
|
|
10279
|
+
onSkip,
|
|
10280
|
+
onAddTime
|
|
10281
|
+
}
|
|
10282
|
+
);
|
|
10283
|
+
}
|
|
9852
10284
|
return /* @__PURE__ */ jsxs(
|
|
9853
10285
|
reactNative.View,
|
|
9854
10286
|
{
|
|
@@ -9946,62 +10378,7 @@ function RestTimer({
|
|
|
9946
10378
|
)
|
|
9947
10379
|
}
|
|
9948
10380
|
),
|
|
9949
|
-
!displayOnly && /* @__PURE__ */
|
|
9950
|
-
/* @__PURE__ */ jsx(
|
|
9951
|
-
reactNative.Pressable,
|
|
9952
|
-
{
|
|
9953
|
-
onPress: onAddTime,
|
|
9954
|
-
style: {
|
|
9955
|
-
backgroundColor: "rgba(255,255,255,0.06)",
|
|
9956
|
-
paddingVertical: 8,
|
|
9957
|
-
paddingHorizontal: 20,
|
|
9958
|
-
borderRadius: 8
|
|
9959
|
-
},
|
|
9960
|
-
accessibilityRole: "button",
|
|
9961
|
-
accessibilityLabel: "Add 30 seconds",
|
|
9962
|
-
testID: "rest-timer-add-time",
|
|
9963
|
-
children: /* @__PURE__ */ jsx(
|
|
9964
|
-
reactNative.Text,
|
|
9965
|
-
{
|
|
9966
|
-
className: "text-text-secondary",
|
|
9967
|
-
style: {
|
|
9968
|
-
fontSize: 11,
|
|
9969
|
-
fontFamily: "Inter, sans-serif",
|
|
9970
|
-
fontWeight: "600"
|
|
9971
|
-
},
|
|
9972
|
-
children: "+30s"
|
|
9973
|
-
}
|
|
9974
|
-
)
|
|
9975
|
-
}
|
|
9976
|
-
),
|
|
9977
|
-
/* @__PURE__ */ jsx(
|
|
9978
|
-
reactNative.Pressable,
|
|
9979
|
-
{
|
|
9980
|
-
onPress: onSkip,
|
|
9981
|
-
style: {
|
|
9982
|
-
backgroundColor: "rgba(255,121,0,0.12)",
|
|
9983
|
-
paddingVertical: 8,
|
|
9984
|
-
paddingHorizontal: 20,
|
|
9985
|
-
borderRadius: 8
|
|
9986
|
-
},
|
|
9987
|
-
accessibilityRole: "button",
|
|
9988
|
-
accessibilityLabel: "Skip rest",
|
|
9989
|
-
testID: "rest-timer-skip",
|
|
9990
|
-
children: /* @__PURE__ */ jsx(
|
|
9991
|
-
reactNative.Text,
|
|
9992
|
-
{
|
|
9993
|
-
style: {
|
|
9994
|
-
fontSize: 11,
|
|
9995
|
-
fontFamily: "Inter, sans-serif",
|
|
9996
|
-
fontWeight: "600",
|
|
9997
|
-
color: BRAND_PRIMARY4
|
|
9998
|
-
},
|
|
9999
|
-
children: "Skip"
|
|
10000
|
-
}
|
|
10001
|
-
)
|
|
10002
|
-
}
|
|
10003
|
-
)
|
|
10004
|
-
] })
|
|
10381
|
+
!displayOnly && /* @__PURE__ */ jsx(RestActions, { onAddTime, onSkip })
|
|
10005
10382
|
]
|
|
10006
10383
|
}
|
|
10007
10384
|
);
|
|
@@ -12991,9 +13368,36 @@ function glowShadow(color) {
|
|
|
12991
13368
|
var DEFAULT_TRACK_COLOR = primitiveColors.charcoal[200];
|
|
12992
13369
|
var DEFAULT_MARKER_COLOR2 = primitiveColors.white;
|
|
12993
13370
|
var TICK_COLOR = t15["text-tertiary"];
|
|
12994
|
-
var
|
|
12995
|
-
|
|
12996
|
-
|
|
13371
|
+
var ZONE_SIZES = {
|
|
13372
|
+
default: {
|
|
13373
|
+
trackHeight: 14,
|
|
13374
|
+
needleOverhang: 6,
|
|
13375
|
+
needleWidth: 4,
|
|
13376
|
+
tickLineNormal: 1.5,
|
|
13377
|
+
tickLineEmphasized: 2,
|
|
13378
|
+
tickLineHeightPad: 4,
|
|
13379
|
+
tickLineTopPad: 2,
|
|
13380
|
+
labelRowHeight: 16,
|
|
13381
|
+
labelMarginTop: 5,
|
|
13382
|
+
tickFont: 9,
|
|
13383
|
+
tickLetterSpacing: 0.5,
|
|
13384
|
+
tickCellWidth: 28
|
|
13385
|
+
},
|
|
13386
|
+
wall: {
|
|
13387
|
+
trackHeight: 24,
|
|
13388
|
+
needleOverhang: 9,
|
|
13389
|
+
needleWidth: 7,
|
|
13390
|
+
tickLineNormal: 2.5,
|
|
13391
|
+
tickLineEmphasized: 3.5,
|
|
13392
|
+
tickLineHeightPad: 6,
|
|
13393
|
+
tickLineTopPad: 3,
|
|
13394
|
+
labelRowHeight: 24,
|
|
13395
|
+
labelMarginTop: 8,
|
|
13396
|
+
tickFont: 14,
|
|
13397
|
+
tickLetterSpacing: 0.5,
|
|
13398
|
+
tickCellWidth: 48
|
|
13399
|
+
}
|
|
13400
|
+
};
|
|
12997
13401
|
function fraction(value, min, max) {
|
|
12998
13402
|
if (max <= min) return 0;
|
|
12999
13403
|
return Math.max(0, Math.min(1, (value - min) / (max - min)));
|
|
@@ -13008,14 +13412,18 @@ function ZoneTrack({
|
|
|
13008
13412
|
marker = null,
|
|
13009
13413
|
ticks,
|
|
13010
13414
|
band = null,
|
|
13011
|
-
|
|
13012
|
-
|
|
13415
|
+
size = "default",
|
|
13416
|
+
trackHeight: trackHeightProp,
|
|
13417
|
+
needleOverhang: needleOverhangProp,
|
|
13013
13418
|
trackColor = DEFAULT_TRACK_COLOR,
|
|
13014
13419
|
className,
|
|
13015
13420
|
style,
|
|
13016
13421
|
accessibilityLabel: accessibilityLabel2,
|
|
13017
13422
|
...props
|
|
13018
13423
|
}) {
|
|
13424
|
+
const s = ZONE_SIZES[size];
|
|
13425
|
+
const trackHeight = trackHeightProp ?? s.trackHeight;
|
|
13426
|
+
const needleOverhang = needleOverhangProp ?? s.needleOverhang;
|
|
13019
13427
|
const isNeedle = marker?.type === "needle";
|
|
13020
13428
|
const markerHeight = isNeedle ? trackHeight + needleOverhang * 2 : trackHeight;
|
|
13021
13429
|
const markerFrac = marker != null ? fraction(marker.value, min, max) : 0;
|
|
@@ -13125,18 +13533,18 @@ function ZoneTrack({
|
|
|
13125
13533
|
position: "absolute",
|
|
13126
13534
|
top: 0,
|
|
13127
13535
|
left: pct(markerFrac),
|
|
13128
|
-
width:
|
|
13536
|
+
width: s.needleWidth,
|
|
13129
13537
|
height: markerHeight,
|
|
13130
|
-
borderRadius:
|
|
13538
|
+
borderRadius: s.needleWidth / 2,
|
|
13131
13539
|
backgroundColor: marker.color ?? DEFAULT_MARKER_COLOR2,
|
|
13132
|
-
transform: [{ translateX: -
|
|
13540
|
+
transform: [{ translateX: -s.needleWidth / 2 }]
|
|
13133
13541
|
}
|
|
13134
13542
|
}
|
|
13135
13543
|
),
|
|
13136
13544
|
ticks?.map((tick, i) => {
|
|
13137
13545
|
const emphasized = tick.emphasized === true;
|
|
13138
13546
|
const lineColor = tick.color ?? (emphasized ? t15["brand-primary"] : TICK_COLOR);
|
|
13139
|
-
const lineWidth = emphasized ?
|
|
13547
|
+
const lineWidth = emphasized ? s.tickLineEmphasized : s.tickLineNormal;
|
|
13140
13548
|
return /* @__PURE__ */ jsx(
|
|
13141
13549
|
reactNative.View,
|
|
13142
13550
|
{
|
|
@@ -13144,10 +13552,10 @@ function ZoneTrack({
|
|
|
13144
13552
|
accessibilityElementsHidden: true,
|
|
13145
13553
|
style: {
|
|
13146
13554
|
position: "absolute",
|
|
13147
|
-
top: (markerHeight - trackHeight) / 2 -
|
|
13555
|
+
top: (markerHeight - trackHeight) / 2 - s.tickLineTopPad,
|
|
13148
13556
|
left: pct(fraction(tick.value, min, max)),
|
|
13149
13557
|
width: lineWidth,
|
|
13150
|
-
height: trackHeight +
|
|
13558
|
+
height: trackHeight + s.tickLineHeightPad,
|
|
13151
13559
|
borderRadius: lineWidth / 2,
|
|
13152
13560
|
backgroundColor: lineColor,
|
|
13153
13561
|
transform: [{ translateX: -lineWidth / 2 }],
|
|
@@ -13158,40 +13566,46 @@ function ZoneTrack({
|
|
|
13158
13566
|
);
|
|
13159
13567
|
})
|
|
13160
13568
|
] }),
|
|
13161
|
-
ticks != null && ticks.length > 0 && /* @__PURE__ */ jsx(
|
|
13162
|
-
|
|
13163
|
-
|
|
13164
|
-
|
|
13165
|
-
|
|
13166
|
-
|
|
13167
|
-
|
|
13168
|
-
|
|
13169
|
-
|
|
13170
|
-
|
|
13171
|
-
|
|
13172
|
-
|
|
13173
|
-
|
|
13174
|
-
|
|
13175
|
-
|
|
13176
|
-
|
|
13177
|
-
|
|
13178
|
-
|
|
13179
|
-
|
|
13180
|
-
|
|
13181
|
-
|
|
13182
|
-
|
|
13183
|
-
|
|
13184
|
-
|
|
13185
|
-
|
|
13186
|
-
|
|
13187
|
-
|
|
13188
|
-
|
|
13189
|
-
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
|
|
13193
|
-
|
|
13194
|
-
|
|
13569
|
+
ticks != null && ticks.length > 0 && /* @__PURE__ */ jsx(
|
|
13570
|
+
reactNative.View,
|
|
13571
|
+
{
|
|
13572
|
+
style: { position: "relative", height: s.labelRowHeight, marginTop: s.labelMarginTop },
|
|
13573
|
+
children: ticks.map((tick, i) => {
|
|
13574
|
+
if (tick.label == null) return null;
|
|
13575
|
+
const emphasized = tick.emphasized === true;
|
|
13576
|
+
const labelColor = tick.color ?? (emphasized ? t15["brand-primary"] : TICK_COLOR);
|
|
13577
|
+
const labelNode = /* @__PURE__ */ jsx(
|
|
13578
|
+
reactNative.Text,
|
|
13579
|
+
{
|
|
13580
|
+
testID: "zone-track-tick-label",
|
|
13581
|
+
style: {
|
|
13582
|
+
fontSize: s.tickFont,
|
|
13583
|
+
letterSpacing: s.tickLetterSpacing,
|
|
13584
|
+
color: labelColor,
|
|
13585
|
+
fontFamily: "monospace",
|
|
13586
|
+
fontWeight: emphasized ? "700" : "400"
|
|
13587
|
+
},
|
|
13588
|
+
children: tick.label
|
|
13589
|
+
}
|
|
13590
|
+
);
|
|
13591
|
+
return /* @__PURE__ */ jsx(
|
|
13592
|
+
reactNative.View,
|
|
13593
|
+
{
|
|
13594
|
+
testID: "zone-track-tick",
|
|
13595
|
+
style: {
|
|
13596
|
+
position: "absolute",
|
|
13597
|
+
left: pct(fraction(tick.value, min, max)),
|
|
13598
|
+
transform: [{ translateX: -s.tickCellWidth / 2 }],
|
|
13599
|
+
width: s.tickCellWidth,
|
|
13600
|
+
alignItems: "center"
|
|
13601
|
+
},
|
|
13602
|
+
children: tick.tooltip != null ? /* @__PURE__ */ jsx(Tooltip, { label: tick.tooltip, placement: "top", children: labelNode }) : labelNode
|
|
13603
|
+
},
|
|
13604
|
+
`label-${i}`
|
|
13605
|
+
);
|
|
13606
|
+
})
|
|
13607
|
+
}
|
|
13608
|
+
)
|
|
13195
13609
|
]
|
|
13196
13610
|
}
|
|
13197
13611
|
);
|
|
@@ -13209,7 +13623,8 @@ function FatigueMeter({
|
|
|
13209
13623
|
thresholds = DEFAULT_THRESHOLDS,
|
|
13210
13624
|
zoneColors = DEFAULT_ZONE_COLORS,
|
|
13211
13625
|
labels = DEFAULT_LABELS,
|
|
13212
|
-
|
|
13626
|
+
size = "default",
|
|
13627
|
+
trackHeight,
|
|
13213
13628
|
needleColor,
|
|
13214
13629
|
className,
|
|
13215
13630
|
...props
|
|
@@ -13230,6 +13645,7 @@ function FatigueMeter({
|
|
|
13230
13645
|
zones,
|
|
13231
13646
|
min: 0,
|
|
13232
13647
|
max,
|
|
13648
|
+
size,
|
|
13233
13649
|
trackHeight,
|
|
13234
13650
|
ticks,
|
|
13235
13651
|
marker: { type: "needle", value, color: needleColor },
|
|
@@ -14296,6 +14712,7 @@ exports.CheckboxGroup = CheckboxGroup;
|
|
|
14296
14712
|
exports.Chip = Chip;
|
|
14297
14713
|
exports.CircleSlashIcon = CircleSlashIcon;
|
|
14298
14714
|
exports.CircularProgress = CircularProgress;
|
|
14715
|
+
exports.CircularTimer = CircularTimer;
|
|
14299
14716
|
exports.Collapse = Collapse;
|
|
14300
14717
|
exports.CollapseButton = CollapseButton;
|
|
14301
14718
|
exports.CollapseContent = CollapseContent;
|