@tastic/hud 0.14.2 → 0.15.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/README.md +110 -79
- package/dist/chrome/index.d.mts +239 -0
- package/dist/chrome/index.d.ts +239 -0
- package/dist/chrome/index.js +772 -0
- package/dist/chrome/index.mjs +44 -0
- package/dist/chunk-OTMMST5M.mjs +645 -0
- package/dist/guide/index.js +3 -3
- package/dist/guide/index.mjs +3 -3
- package/dist/index.d.mts +21 -252
- package/dist/index.d.ts +21 -252
- package/dist/index.js +20 -20
- package/dist/index.mjs +186 -791
- package/package.json +8 -1
- package/src/AchievementCatalogSection.tsx +1 -1
- package/src/AchievementRow.tsx +9 -9
- package/src/BaseSettingsDialog.tsx +31 -31
- package/src/BaseStatsScreen.tsx +13 -13
- package/src/ConfirmDialog.tsx +2 -2
- package/src/ControlSchemePicker.tsx +9 -9
- package/src/CornerActionButtons.tsx +6 -6
- package/src/CornerStatusBadges.tsx +8 -8
- package/src/InlineColorPicker.tsx +30 -30
- package/src/LabeledDropdown.tsx +8 -8
- package/src/PlayerSetupPanel.tsx +25 -25
- package/src/PopoverBody.tsx +16 -16
- package/src/PressAwayOverlay.tsx +2 -2
- package/src/ReadyButton.tsx +3 -3
- package/src/SectionedDropdown.tsx +31 -31
- package/src/SharedActionBand.tsx +6 -6
- package/src/SkiaGate.tsx +5 -5
- package/src/SkiaGate.web.tsx +2 -2
- package/src/StaggeredWord.tsx +9 -9
- package/src/StatRow.tsx +3 -3
- package/src/StatSection.tsx +3 -3
- package/src/TriggerGauge.tsx +54 -54
- package/src/TriggerGaugeHost.tsx +2 -2
- package/src/chrome/index.ts +22 -0
- package/src/fonts.ts +1 -1
- package/src/guide/DirectionKeysHint.tsx +3 -3
- package/src/guide/GuideProvider.tsx +7 -7
- package/src/guide/HowToPlayDialog.tsx +7 -7
- package/src/guide/SeatDiagram.tsx +3 -3
- package/src/guide/SwipeHint.tsx +4 -4
- package/src/guide/guideSlice.ts +7 -7
- package/src/guide/index.ts +4 -3
- package/src/guide/types.ts +1 -1
- package/src/index.ts +1 -1
- package/src/loadSkiaWeb.ts +2 -2
- package/src/loadSkiaWeb.web.ts +2 -2
- package/src/overlayCard.ts +3 -3
- package/src/useAutoAlign.ts +23 -23
- package/src/usePopoverHost.ts +1 -1
- package/src/useZoneClampedAlign.ts +23 -23
package/dist/index.mjs
CHANGED
|
@@ -1,129 +1,52 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SkiaGate
|
|
3
3
|
} from "./chunk-XEOFR236.mjs";
|
|
4
|
+
import {
|
|
5
|
+
AchievementCatalogSection,
|
|
6
|
+
AchievementRow,
|
|
7
|
+
ActivityStatSection,
|
|
8
|
+
BaseSettingsDialog,
|
|
9
|
+
BaseStatsScreen,
|
|
10
|
+
ConfirmDialog,
|
|
11
|
+
ContentGutter,
|
|
12
|
+
CornerActionButtons,
|
|
13
|
+
LOCKED_BADGE_COLOR,
|
|
14
|
+
MONO_FONT,
|
|
15
|
+
RotatedFrame,
|
|
16
|
+
StaggeredWord,
|
|
17
|
+
StatRow,
|
|
18
|
+
StatSection,
|
|
19
|
+
UpdateDialog,
|
|
20
|
+
getStaggeredWordDuration
|
|
21
|
+
} from "./chunk-OTMMST5M.mjs";
|
|
4
22
|
import {
|
|
5
23
|
getOverlayCardColors,
|
|
6
24
|
overlayActionStyles,
|
|
7
25
|
overlayStyles
|
|
8
26
|
} from "./chunk-32U2OIRW.mjs";
|
|
9
27
|
|
|
10
|
-
// src/AchievementCatalogSection.tsx
|
|
11
|
-
import { useAutoPaperTheme as useAutoPaperTheme2 } from "@rific/auto-paper";
|
|
12
|
-
import { Fragment } from "react";
|
|
13
|
-
import { StyleSheet as StyleSheet2 } from "react-native";
|
|
14
|
-
import { Text as Text2 } from "react-native-paper";
|
|
15
|
-
|
|
16
|
-
// src/AchievementRow.tsx
|
|
17
|
-
import { useAutoPaperTheme } from "@rific/auto-paper";
|
|
18
|
-
import { StyleSheet, View } from "react-native";
|
|
19
|
-
import { Icon, ProgressBar, Text } from "react-native-paper";
|
|
20
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
-
var LOCKED_BADGE_COLOR = "rgba(128,128,128,0.3)";
|
|
22
|
-
function AchievementRow({ icon, title, description, badgeColor, unlockedLabel, checkColor, progress, deviceMarker, fg: fgOverride, fgMuted: fgMutedOverride, sectionBg: sectionBgOverride }) {
|
|
23
|
-
const { dark } = useAutoPaperTheme();
|
|
24
|
-
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
25
|
-
const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
|
|
26
|
-
const sectionBg = sectionBgOverride ?? (dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)");
|
|
27
|
-
const unlocked = unlockedLabel !== void 0;
|
|
28
|
-
return /* @__PURE__ */ jsxs(View, { style: [styles.row, { backgroundColor: sectionBg }], children: [
|
|
29
|
-
/* @__PURE__ */ jsx(View, { style: [styles.badge, { backgroundColor: badgeColor }], children: /* @__PURE__ */ jsx(Icon, { source: icon, size: 20, color: unlocked ? "#000000" : fgMuted }) }),
|
|
30
|
-
/* @__PURE__ */ jsxs(View, { style: styles.text, children: [
|
|
31
|
-
/* @__PURE__ */ jsx(Text, { variant: "bodyLarge", style: [styles.boldText, { color: fg }], children: title }),
|
|
32
|
-
/* @__PURE__ */ jsx(Text, { variant: "bodySmall", style: { color: fgMuted }, children: description }),
|
|
33
|
-
deviceMarker && /* @__PURE__ */ jsxs(View, { style: styles.deviceMarker, children: [
|
|
34
|
-
/* @__PURE__ */ jsx(Icon, { source: "earth", size: 11, color: fgMuted }),
|
|
35
|
-
/* @__PURE__ */ jsx(Text, { variant: "labelSmall", style: { color: fgMuted }, children: "Same for every profile" })
|
|
36
|
-
] }),
|
|
37
|
-
!unlocked && progress !== void 0 && /* @__PURE__ */ jsx(ProgressBar, { progress, color: checkColor ?? badgeColor, style: styles.progressBar })
|
|
38
|
-
] }),
|
|
39
|
-
unlocked ? /* @__PURE__ */ jsxs(View, { style: styles.status, children: [
|
|
40
|
-
/* @__PURE__ */ jsx(Icon, { source: "check-circle", size: 20, color: checkColor ?? badgeColor }),
|
|
41
|
-
/* @__PURE__ */ jsx(Text, { variant: "labelSmall", style: { color: fgMuted }, children: unlockedLabel })
|
|
42
|
-
] }) : /* @__PURE__ */ jsx(Icon, { source: "lock-outline", size: 20, color: fgMuted })
|
|
43
|
-
] });
|
|
44
|
-
}
|
|
45
|
-
var styles = StyleSheet.create({
|
|
46
|
-
badge: {
|
|
47
|
-
alignItems: "center",
|
|
48
|
-
borderRadius: 10,
|
|
49
|
-
height: 36,
|
|
50
|
-
justifyContent: "center",
|
|
51
|
-
width: 36
|
|
52
|
-
},
|
|
53
|
-
boldText: {
|
|
54
|
-
fontWeight: "bold"
|
|
55
|
-
},
|
|
56
|
-
deviceMarker: {
|
|
57
|
-
alignItems: "center",
|
|
58
|
-
flexDirection: "row",
|
|
59
|
-
gap: 4
|
|
60
|
-
},
|
|
61
|
-
progressBar: {
|
|
62
|
-
borderRadius: 4,
|
|
63
|
-
height: 6,
|
|
64
|
-
marginTop: 6
|
|
65
|
-
},
|
|
66
|
-
row: {
|
|
67
|
-
alignItems: "center",
|
|
68
|
-
borderRadius: 12,
|
|
69
|
-
flexDirection: "row",
|
|
70
|
-
gap: 12,
|
|
71
|
-
padding: 12
|
|
72
|
-
},
|
|
73
|
-
status: {
|
|
74
|
-
alignItems: "center",
|
|
75
|
-
gap: 2,
|
|
76
|
-
width: 64
|
|
77
|
-
},
|
|
78
|
-
text: {
|
|
79
|
-
flex: 1,
|
|
80
|
-
gap: 2
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
// src/fonts.ts
|
|
85
|
-
import { Platform } from "react-native";
|
|
86
|
-
var MONO_FONT = Platform.select({ ios: "Menlo", android: "monospace", default: "monospace" });
|
|
87
|
-
|
|
88
|
-
// src/AchievementCatalogSection.tsx
|
|
89
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
90
|
-
function AchievementCatalogSection({ rows, label = "ALL ACHIEVEMENTS", fg, fgMuted: fgMutedOverride }) {
|
|
91
|
-
const { dark } = useAutoPaperTheme2();
|
|
92
|
-
const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
|
|
93
|
-
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
94
|
-
/* @__PURE__ */ jsx2(Text2, { variant: "labelMedium", style: [styles2.label, { color: fgMuted, fontFamily: MONO_FONT }], children: label }),
|
|
95
|
-
rows.map((row) => /* @__PURE__ */ jsx2(AchievementRow, { icon: row.icon, title: row.title, description: row.description, badgeColor: row.unlockedAt !== void 0 ? row.tierColor : LOCKED_BADGE_COLOR, checkColor: row.tierColor, unlockedLabel: row.unlockedLabel, progress: row.progress, deviceMarker: row.deviceMarker, fg, fgMuted: fgMutedOverride }, row.id))
|
|
96
|
-
] });
|
|
97
|
-
}
|
|
98
|
-
var styles2 = StyleSheet2.create({
|
|
99
|
-
label: {
|
|
100
|
-
letterSpacing: 2,
|
|
101
|
-
marginTop: 8
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
|
|
105
28
|
// src/AchievementUnlockList.tsx
|
|
106
|
-
import { getContrastColor, useAutoPaperTheme
|
|
29
|
+
import { getContrastColor, useAutoPaperTheme } from "@rific/auto-paper";
|
|
107
30
|
import { ProfileChip } from "@tastic/profile";
|
|
108
|
-
import { StyleSheet
|
|
109
|
-
import { Icon
|
|
110
|
-
import { jsx
|
|
31
|
+
import { StyleSheet, View } from "react-native";
|
|
32
|
+
import { Icon, Text } from "react-native-paper";
|
|
33
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
111
34
|
var AVATAR_SIZE = 18;
|
|
112
35
|
function AchievementUnlockList({ unlocks, seats, owners, tierColors, fg: fgOverride, style }) {
|
|
113
|
-
const { dark } =
|
|
36
|
+
const { dark } = useAutoPaperTheme();
|
|
114
37
|
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
115
38
|
const rows = seats.flatMap((seat) => (unlocks[seat] ?? []).map((achievement) => ({ seat, achievement })));
|
|
116
39
|
if (rows.length === 0) return null;
|
|
117
|
-
return /* @__PURE__ */
|
|
40
|
+
return /* @__PURE__ */ jsx(View, { style: [styles.list, style], children: rows.map(({ seat, achievement }) => {
|
|
118
41
|
const owner = owners[seat];
|
|
119
|
-
return /* @__PURE__ */
|
|
120
|
-
owner.profile ? /* @__PURE__ */
|
|
121
|
-
/* @__PURE__ */
|
|
122
|
-
/* @__PURE__ */
|
|
42
|
+
return /* @__PURE__ */ jsxs(View, { style: styles.row, children: [
|
|
43
|
+
owner.profile ? /* @__PURE__ */ jsx(ProfileChip, { profile: owner.profile, size: AVATAR_SIZE }) : /* @__PURE__ */ jsx(View, { style: [styles.circle, { backgroundColor: owner.color }], children: /* @__PURE__ */ jsx(Icon, { source: "account", size: 10, color: getContrastColor(owner.color) }) }),
|
|
44
|
+
/* @__PURE__ */ jsx(View, { style: [styles.circle, { backgroundColor: tierColors[achievement.tier] }], children: /* @__PURE__ */ jsx(Icon, { source: achievement.icon, size: 12, color: "#000000" }) }),
|
|
45
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.label, { color: fg }], numberOfLines: 1, children: achievement.title })
|
|
123
46
|
] }, `${seat}-${achievement.id}`);
|
|
124
47
|
}) });
|
|
125
48
|
}
|
|
126
|
-
var
|
|
49
|
+
var styles = StyleSheet.create({
|
|
127
50
|
circle: {
|
|
128
51
|
alignItems: "center",
|
|
129
52
|
borderRadius: AVATAR_SIZE / 2,
|
|
@@ -146,517 +69,49 @@ var styles3 = StyleSheet3.create({
|
|
|
146
69
|
}
|
|
147
70
|
});
|
|
148
71
|
|
|
149
|
-
// src/StatRow.tsx
|
|
150
|
-
import { useAutoPaperTheme as useAutoPaperTheme4 } from "@rific/auto-paper";
|
|
151
|
-
import { StyleSheet as StyleSheet4, View as View3 } from "react-native";
|
|
152
|
-
import { Text as Text4 } from "react-native-paper";
|
|
153
|
-
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
154
|
-
function StatRow({ label, value, fg: fgOverride, fgMuted: fgMutedOverride }) {
|
|
155
|
-
const { dark } = useAutoPaperTheme4();
|
|
156
|
-
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
157
|
-
const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
|
|
158
|
-
return /* @__PURE__ */ jsxs4(View3, { style: styles4.row, children: [
|
|
159
|
-
/* @__PURE__ */ jsx4(Text4, { variant: "bodyMedium", style: { color: fgMuted }, children: label }),
|
|
160
|
-
/* @__PURE__ */ jsx4(Text4, { variant: "bodyMedium", style: [styles4.value, { color: fg }], children: value })
|
|
161
|
-
] });
|
|
162
|
-
}
|
|
163
|
-
var styles4 = StyleSheet4.create({
|
|
164
|
-
row: {
|
|
165
|
-
alignItems: "center",
|
|
166
|
-
flexDirection: "row",
|
|
167
|
-
justifyContent: "space-between"
|
|
168
|
-
},
|
|
169
|
-
value: {
|
|
170
|
-
fontWeight: "bold"
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// src/StatSection.tsx
|
|
175
|
-
import { useAutoPaperTheme as useAutoPaperTheme5 } from "@rific/auto-paper";
|
|
176
|
-
import { StyleSheet as StyleSheet5, View as View4 } from "react-native";
|
|
177
|
-
import { Text as Text5 } from "react-native-paper";
|
|
178
|
-
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
179
|
-
function StatSection({ label, fg: fgOverride, sectionBg: sectionBgOverride, children }) {
|
|
180
|
-
const { dark } = useAutoPaperTheme5();
|
|
181
|
-
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
182
|
-
const sectionBg = sectionBgOverride ?? (dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.04)");
|
|
183
|
-
return /* @__PURE__ */ jsxs5(View4, { style: [styles5.section, { backgroundColor: sectionBg }], children: [
|
|
184
|
-
/* @__PURE__ */ jsx5(Text5, { variant: "labelMedium", style: [styles5.label, { color: fg }], children: label }),
|
|
185
|
-
children
|
|
186
|
-
] });
|
|
187
|
-
}
|
|
188
|
-
var styles5 = StyleSheet5.create({
|
|
189
|
-
// Bold and full-contrast (fg), not the original fontless/fgMuted treatment — that read at the
|
|
190
|
-
// same visual weight as StatRow's own row labels below it (both used the identical muted color;
|
|
191
|
-
// letterSpacing/variant alone didn't separate them enough to read as "this is the group header"
|
|
192
|
-
// at a glance). Matches AchievementRow's own title styling (bold + fg) below it in the same list.
|
|
193
|
-
label: {
|
|
194
|
-
fontWeight: "bold",
|
|
195
|
-
letterSpacing: 2
|
|
196
|
-
},
|
|
197
|
-
section: {
|
|
198
|
-
borderRadius: 12,
|
|
199
|
-
gap: 8,
|
|
200
|
-
padding: 16
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
// src/ActivityStatSection.tsx
|
|
205
|
-
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
206
|
-
function ActivityStatSection({ stats }) {
|
|
207
|
-
return /* @__PURE__ */ jsxs6(StatSection, { label: "ACTIVITY", children: [
|
|
208
|
-
/* @__PURE__ */ jsx6(StatRow, { label: "Days Played", value: String(stats.distinctDaysPlayed) }),
|
|
209
|
-
/* @__PURE__ */ jsx6(StatRow, { label: "Day Streak", value: String(stats.currentDayStreak) }),
|
|
210
|
-
/* @__PURE__ */ jsx6(StatRow, { label: "Best Day Streak", value: String(stats.bestDayStreak) })
|
|
211
|
-
] });
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// src/BaseSettingsDialog.tsx
|
|
215
|
-
import { AutoAppearancePicker, Dialog, useAutoPaperTheme as useAutoPaperTheme7 } from "@rific/auto-paper";
|
|
216
|
-
import { Button as Button2, SoundContext, TouchableRipple, useHapticSettings, useSoundSettings, useVibration } from "@rific/feedback-press";
|
|
217
|
-
import { useUpdater } from "@rific/updater";
|
|
218
|
-
import { useIsTouchPrimaryDevice, useRotation as useRotation2 } from "@tastic/core";
|
|
219
|
-
import { useContext, useState as useState2 } from "react";
|
|
220
|
-
import { Platform as Platform2, ScrollView, StyleSheet as StyleSheet8, View as View7 } from "react-native";
|
|
221
|
-
import { Icon as Icon4, Portal as Portal3, Text as Text7 } from "react-native-paper";
|
|
222
|
-
|
|
223
|
-
// src/ConfirmDialog.tsx
|
|
224
|
-
import { useAutoPaperTheme as useAutoPaperTheme6 } from "@rific/auto-paper";
|
|
225
|
-
import { Button } from "@rific/feedback-press";
|
|
226
|
-
import { toRotationStyle } from "@tastic/core";
|
|
227
|
-
import { StyleSheet as StyleSheet6, View as View5 } from "react-native";
|
|
228
|
-
import { Icon as Icon3, Portal, Text as Text6 } from "react-native-paper";
|
|
229
|
-
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
230
|
-
function ConfirmDialog({ visible, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, rotation = 0, icon = "help-circle-outline", destructive = false }) {
|
|
231
|
-
const { dark, colors } = useAutoPaperTheme6();
|
|
232
|
-
if (!visible) return null;
|
|
233
|
-
const { fg, cardBg, cardBorder } = getOverlayCardColors(dark);
|
|
234
|
-
const accent = destructive ? colors.danger : colors.primary;
|
|
235
|
-
const onAccent = destructive ? colors.onDanger : colors.onPrimary;
|
|
236
|
-
const showCancel = cancelLabel !== void 0 && onCancel !== void 0;
|
|
237
|
-
return /* @__PURE__ */ jsx7(Portal, { children: /* @__PURE__ */ jsx7(View5, { style: [overlayStyles.overlay, styles6.overlay], children: /* @__PURE__ */ jsxs7(View5, { style: [overlayStyles.card, styles6.card, { backgroundColor: cardBg, borderColor: cardBorder }, toRotationStyle(rotation)], children: [
|
|
238
|
-
/* @__PURE__ */ jsx7(Icon3, { source: icon, size: 64, color: accent }),
|
|
239
|
-
/* @__PURE__ */ jsx7(Text6, { variant: "headlineLarge", style: [styles6.title, { color: accent }], children: title }),
|
|
240
|
-
/* @__PURE__ */ jsx7(Text6, { variant: "bodyLarge", style: [styles6.body, { color: fg }], children: message }),
|
|
241
|
-
/* @__PURE__ */ jsxs7(View5, { style: overlayActionStyles.actions, children: [
|
|
242
|
-
showCancel && /* @__PURE__ */ jsx7(Button, { mode: "outlined", onPress: onCancel, style: overlayActionStyles.actionButton, children: cancelLabel }),
|
|
243
|
-
/* @__PURE__ */ jsx7(Button, { mode: "contained", onPress: onConfirm, style: showCancel ? overlayActionStyles.actionButton : overlayActionStyles.actionButtonSingle, buttonColor: accent, textColor: onAccent, children: confirmLabel })
|
|
244
|
-
] })
|
|
245
|
-
] }) }) });
|
|
246
|
-
}
|
|
247
|
-
var styles6 = StyleSheet6.create({
|
|
248
|
-
body: {
|
|
249
|
-
textAlign: "center"
|
|
250
|
-
},
|
|
251
|
-
// Additive on top of overlayCard's own shared `card` shape (borderRadius/borderWidth/gap/maxWidth/
|
|
252
|
-
// padding) — claims its full allowance instead of just maxWidth alone, which only caps how wide the
|
|
253
|
-
// card is allowed to get - without an explicit width, a flex-centered child sizes to its own content
|
|
254
|
-
// instead of stretching, so the card (and therefore actions below) can end up narrower than the
|
|
255
|
-
// screen actually has room for, needlessly forcing the action row to wrap sooner than it has to.
|
|
256
|
-
card: {
|
|
257
|
-
width: "100%"
|
|
258
|
-
},
|
|
259
|
-
// Additive on top of overlayCard's own shared `overlay` backdrop — gives the card room to breathe
|
|
260
|
-
// from the screen edges now that it reliably stretches to fill its full width allowance (see
|
|
261
|
-
// card's own width comment) - without this, width: '100%' would run the card edge-to-edge on a
|
|
262
|
-
// screen narrower than maxWidth's own 360.
|
|
263
|
-
overlay: {
|
|
264
|
-
paddingHorizontal: 24
|
|
265
|
-
},
|
|
266
|
-
// textAlign is load-bearing, not decorative, once `title` wraps to more than one line - centered
|
|
267
|
-
// within card's own alignItems: 'center' only centers the Text's block as a whole; each wrapped
|
|
268
|
-
// line inside that block still defaults to left-aligned without this.
|
|
269
|
-
title: {
|
|
270
|
-
fontWeight: "bold",
|
|
271
|
-
textAlign: "center"
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
// src/RotatedFrame.tsx
|
|
276
|
-
import { FakeLandscapeView, rotateDimensions, rotateInsets, useRotation } from "@tastic/core";
|
|
277
|
-
import { useMemo } from "react";
|
|
278
|
-
import { StyleSheet as StyleSheet7, useWindowDimensions, View as View6 } from "react-native";
|
|
279
|
-
import { Portal as Portal2 } from "react-native-paper";
|
|
280
|
-
import { SafeAreaInsetsContext, useSafeAreaInsets } from "react-native-safe-area-context";
|
|
281
|
-
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
282
|
-
function RotatedFrame({ children, rotation: rotationOverride, rotatedSafeArea = false, portalHost = false, passThrough = false, maxContentWidth }) {
|
|
283
|
-
const ambientRotation = useRotation();
|
|
284
|
-
const rotation = rotationOverride ?? ambientRotation;
|
|
285
|
-
const window = useWindowDimensions();
|
|
286
|
-
const { width, height } = rotateDimensions(window.width, window.height, rotation);
|
|
287
|
-
const rawInsets = useSafeAreaInsets();
|
|
288
|
-
const insets = useMemo(() => rotatedSafeArea ? rotateInsets(rawInsets, rotation) : rawInsets, [rotatedSafeArea, rawInsets, rotation]);
|
|
289
|
-
const cappedWidth = maxContentWidth !== void 0 && width > height ? maxContentWidth : void 0;
|
|
290
|
-
const column = /* @__PURE__ */ jsx8(View6, { style: [styles7.column, cappedWidth !== void 0 && { maxWidth: cappedWidth }], pointerEvents: passThrough ? "box-none" : void 0, children });
|
|
291
|
-
return /* @__PURE__ */ jsx8(FakeLandscapeView, { rotation, passThrough, style: styles7.root, children: /* @__PURE__ */ jsx8(SafeAreaInsetsContext.Provider, { value: insets, children: portalHost ? /* @__PURE__ */ jsx8(Portal2.Host, { children: column }) : column }) });
|
|
292
|
-
}
|
|
293
|
-
var styles7 = StyleSheet7.create({
|
|
294
|
-
// A centred column: full width up to `maxWidth`, so a capped frame sits in the middle of a wider window.
|
|
295
|
-
column: {
|
|
296
|
-
alignSelf: "center",
|
|
297
|
-
flex: 1,
|
|
298
|
-
width: "100%"
|
|
299
|
-
},
|
|
300
|
-
root: {
|
|
301
|
-
flex: 1
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
// src/useUpdateConfirmBridge.ts
|
|
306
|
-
import { useCallback, useRef, useState } from "react";
|
|
307
|
-
function useUpdateConfirmBridge() {
|
|
308
|
-
const [manifest, setManifest] = useState(null);
|
|
309
|
-
const resolveRef = useRef(null);
|
|
310
|
-
const onConfirm = useCallback(
|
|
311
|
-
(next) => new Promise((resolve) => {
|
|
312
|
-
resolveRef.current = resolve;
|
|
313
|
-
setManifest(next);
|
|
314
|
-
}),
|
|
315
|
-
[]
|
|
316
|
-
);
|
|
317
|
-
const respond = useCallback((confirmed) => {
|
|
318
|
-
setManifest(null);
|
|
319
|
-
resolveRef.current?.(confirmed);
|
|
320
|
-
resolveRef.current = null;
|
|
321
|
-
}, []);
|
|
322
|
-
return { manifest, onConfirm, respond };
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// src/BaseSettingsDialog.tsx
|
|
326
|
-
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
327
|
-
function SettingIcon({ source, color, containerColor }) {
|
|
328
|
-
return /* @__PURE__ */ jsx9(View7, { style: [styles8.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ jsx9(Icon4, { source, size: 18, color }) });
|
|
329
|
-
}
|
|
330
|
-
function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, onShowHowToPlay, children }) {
|
|
331
|
-
const { colors } = useAutoPaperTheme7();
|
|
332
|
-
const ambientRotation = useRotation2();
|
|
333
|
-
const rotation = rotationOverride ?? ambientRotation;
|
|
334
|
-
const isTouchPrimary = useIsTouchPrimaryDevice();
|
|
335
|
-
const { settings: hapticSettings, set: setHapticSettings } = useHapticSettings();
|
|
336
|
-
const { settings: soundSettings, set: setSoundSettings } = useSoundSettings();
|
|
337
|
-
const sound = useContext(SoundContext);
|
|
338
|
-
const { forceShort: forceHapticFeedback } = useVibration();
|
|
339
|
-
const [infoMessage, setInfoMessage] = useState2(null);
|
|
340
|
-
const { manifest: updateManifest, onConfirm: onUpdateConfirm, respond: respondToUpdateConfirm } = useUpdateConfirmBridge();
|
|
341
|
-
const { check, checking, updateReady } = useUpdater({
|
|
342
|
-
autoCheck: false,
|
|
343
|
-
autoPrompt: false,
|
|
344
|
-
onConfirm: onUpdateConfirm,
|
|
345
|
-
onError: onUpdateError,
|
|
346
|
-
onInfo: (title, message) => setInfoMessage({ title, message })
|
|
347
|
-
});
|
|
348
|
-
const updateManifestDate = updateManifest ? new Date(updateManifest.createdAt) : null;
|
|
349
|
-
const [opened, setOpened] = useState2(visible);
|
|
350
|
-
if (visible && !opened) setOpened(true);
|
|
351
|
-
const showLockOrientation = isTouchPrimary && lockOrientation !== void 0 && onLockOrientationChange !== void 0;
|
|
352
|
-
const showEdgeGuard = Platform2.OS === "ios" && deferBottomEdgeGestures !== void 0 && onDeferBottomEdgeGestures !== void 0;
|
|
353
|
-
const showHaptics = Platform2.OS !== "web" && !hideHaptics;
|
|
354
|
-
const showSound = !hideSound;
|
|
355
|
-
const showAppearance = !hideAppearance;
|
|
356
|
-
const showUpdateCheck = Platform2.OS !== "web" && !hideUpdateCheck;
|
|
357
|
-
if (!opened) return null;
|
|
358
|
-
return /* @__PURE__ */ jsx9(Portal3, { children: /* @__PURE__ */ jsxs8(RotatedFrame, { rotation, rotatedSafeArea: true, portalHost: true, passThrough: true, children: [
|
|
359
|
-
/* @__PURE__ */ jsxs8(Dialog, { visible, onDismiss, style: styles8.dialog, children: [
|
|
360
|
-
/* @__PURE__ */ jsx9(Dialog.Title, { children: "Settings" }),
|
|
361
|
-
/* @__PURE__ */ jsx9(Dialog.ScrollArea, { style: styles8.scrollArea, children: /* @__PURE__ */ jsxs8(ScrollView, { contentContainerStyle: styles8.content, showsVerticalScrollIndicator: false, children: [
|
|
362
|
-
/* @__PURE__ */ jsxs8(View7, { style: styles8.toggleGroup, children: [
|
|
363
|
-
showLockOrientation && /* @__PURE__ */ jsx9(TouchableRipple, { onPress: () => onLockOrientationChange(!lockOrientation), style: styles8.toggleButton, accessibilityLabel: `Lock orientation ${lockOrientation ? "on" : "off"}`, children: /* @__PURE__ */ jsxs8(View7, { style: styles8.toggleContent, children: [
|
|
364
|
-
/* @__PURE__ */ jsx9(SettingIcon, { source: lockOrientation ? "lock" : "lock-open-variant-outline", color: lockOrientation ? colors.tertiary : colors.onSurfaceVariant, containerColor: lockOrientation ? colors.tertiaryContainer : colors.surfaceVariant }),
|
|
365
|
-
/* @__PURE__ */ jsxs8(View7, { style: styles8.flexShrink, children: [
|
|
366
|
-
/* @__PURE__ */ jsx9(Text7, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Lock Orientation" }),
|
|
367
|
-
/* @__PURE__ */ jsx9(Text7, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Pins the current layout" })
|
|
368
|
-
] })
|
|
369
|
-
] }) }),
|
|
370
|
-
showEdgeGuard && /* @__PURE__ */ jsx9(TouchableRipple, { onPress: () => onDeferBottomEdgeGestures(!deferBottomEdgeGestures), style: styles8.toggleButton, accessibilityLabel: `Edge guard ${deferBottomEdgeGestures ? "on" : "off"}`, children: /* @__PURE__ */ jsxs8(View7, { style: styles8.toggleContent, children: [
|
|
371
|
-
/* @__PURE__ */ jsx9(SettingIcon, { source: deferBottomEdgeGestures ? "shield-check-outline" : "shield-off-outline", color: deferBottomEdgeGestures ? colors.tertiary : colors.onSurfaceVariant, containerColor: deferBottomEdgeGestures ? colors.tertiaryContainer : colors.surfaceVariant }),
|
|
372
|
-
/* @__PURE__ */ jsxs8(View7, { style: styles8.flexShrink, children: [
|
|
373
|
-
/* @__PURE__ */ jsx9(Text7, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Edge Guard" }),
|
|
374
|
-
/* @__PURE__ */ jsx9(Text7, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Requires a second swipe" })
|
|
375
|
-
] })
|
|
376
|
-
] }) }),
|
|
377
|
-
(showSound || showHaptics) && /* @__PURE__ */ jsxs8(View7, { style: styles8.toggleRow, children: [
|
|
378
|
-
showSound && /* @__PURE__ */ jsx9(
|
|
379
|
-
TouchableRipple,
|
|
380
|
-
{
|
|
381
|
-
soundDisabled: true,
|
|
382
|
-
onPress: () => {
|
|
383
|
-
const enabled = !soundSettings.enabled;
|
|
384
|
-
setSoundSettings({ enabled });
|
|
385
|
-
if (enabled) sound.selection?.();
|
|
386
|
-
},
|
|
387
|
-
style: styles8.toggleButtonInRow,
|
|
388
|
-
accessibilityLabel: `Sound ${soundSettings.enabled ? "on" : "off"}`,
|
|
389
|
-
children: /* @__PURE__ */ jsxs8(View7, { style: styles8.toggleContent, children: [
|
|
390
|
-
/* @__PURE__ */ jsx9(SettingIcon, { source: soundSettings.enabled ? "volume-high" : "volume-off", color: soundSettings.enabled ? colors.tertiary : colors.onSurfaceVariant, containerColor: soundSettings.enabled ? colors.tertiaryContainer : colors.surfaceVariant }),
|
|
391
|
-
/* @__PURE__ */ jsx9(Text7, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Sound" })
|
|
392
|
-
] })
|
|
393
|
-
}
|
|
394
|
-
),
|
|
395
|
-
showHaptics && /* @__PURE__ */ jsx9(
|
|
396
|
-
TouchableRipple,
|
|
397
|
-
{
|
|
398
|
-
hapticDisabled: true,
|
|
399
|
-
onPress: () => {
|
|
400
|
-
const vibrate = !hapticSettings.vibrate;
|
|
401
|
-
setHapticSettings({ vibrate });
|
|
402
|
-
if (vibrate) forceHapticFeedback();
|
|
403
|
-
},
|
|
404
|
-
style: styles8.toggleButtonInRow,
|
|
405
|
-
accessibilityLabel: `Haptics ${hapticSettings.vibrate ? "on" : "off"}`,
|
|
406
|
-
children: /* @__PURE__ */ jsxs8(View7, { style: styles8.toggleContent, children: [
|
|
407
|
-
/* @__PURE__ */ jsx9(SettingIcon, { source: hapticSettings.vibrate ? "vibrate" : "vibrate-off", color: hapticSettings.vibrate ? colors.tertiary : colors.onSurfaceVariant, containerColor: hapticSettings.vibrate ? colors.tertiaryContainer : colors.surfaceVariant }),
|
|
408
|
-
/* @__PURE__ */ jsx9(Text7, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "Haptics" })
|
|
409
|
-
] })
|
|
410
|
-
}
|
|
411
|
-
)
|
|
412
|
-
] }),
|
|
413
|
-
onShowHowToPlay && /* @__PURE__ */ jsx9(
|
|
414
|
-
TouchableRipple,
|
|
415
|
-
{
|
|
416
|
-
onPress: () => {
|
|
417
|
-
onDismiss();
|
|
418
|
-
onShowHowToPlay();
|
|
419
|
-
},
|
|
420
|
-
style: styles8.toggleButton,
|
|
421
|
-
accessibilityLabel: "How to Play",
|
|
422
|
-
children: /* @__PURE__ */ jsxs8(View7, { style: styles8.toggleContent, children: [
|
|
423
|
-
/* @__PURE__ */ jsx9(SettingIcon, { source: "help-circle-outline", color: colors.secondary, containerColor: colors.secondaryContainer }),
|
|
424
|
-
/* @__PURE__ */ jsxs8(View7, { style: styles8.flexShrink, children: [
|
|
425
|
-
/* @__PURE__ */ jsx9(Text7, { variant: "bodyLarge", style: { color: colors.onSurface }, children: "How to Play" }),
|
|
426
|
-
/* @__PURE__ */ jsx9(Text7, { variant: "bodySmall", numberOfLines: 1, style: { color: colors.onSurfaceVariant }, children: "Replay the quick intro" })
|
|
427
|
-
] })
|
|
428
|
-
] })
|
|
429
|
-
}
|
|
430
|
-
)
|
|
431
|
-
] }),
|
|
432
|
-
showAppearance && /* @__PURE__ */ jsxs8(View7, { style: styles8.section, children: [
|
|
433
|
-
/* @__PURE__ */ jsx9(Text7, { variant: "labelMedium", style: [styles8.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
|
|
434
|
-
/* @__PURE__ */ jsx9(AutoAppearancePicker, { showLabels: false })
|
|
435
|
-
] }),
|
|
436
|
-
children,
|
|
437
|
-
showUpdateCheck && /* @__PURE__ */ jsxs8(View7, { style: styles8.section, children: [
|
|
438
|
-
/* @__PURE__ */ jsxs8(Text7, { variant: "labelSmall", style: [styles8.sectionLabel, { color: colors.onSurfaceVariant }], children: [
|
|
439
|
-
"VERSION ",
|
|
440
|
-
version,
|
|
441
|
-
updateReady ? " \xB7 UPDATE READY" : ""
|
|
442
|
-
] }),
|
|
443
|
-
/* @__PURE__ */ jsx9(Button2, { mode: "outlined", onPress: check, loading: checking, disabled: checking, children: "Check for Updates" })
|
|
444
|
-
] })
|
|
445
|
-
] }) })
|
|
446
|
-
] }),
|
|
447
|
-
/* @__PURE__ */ jsx9(ConfirmDialog, { visible: !!updateManifest, title: "Update Available", message: updateManifestDate ? `Released ${updateManifestDate.toLocaleDateString()} at ${updateManifestDate.toLocaleTimeString()}` : "", confirmLabel: "Restart", cancelLabel: "Later", icon: "update", rotation: 0, onConfirm: () => respondToUpdateConfirm(true), onCancel: () => respondToUpdateConfirm(false) }),
|
|
448
|
-
/* @__PURE__ */ jsx9(ConfirmDialog, { visible: !!infoMessage, title: infoMessage?.title ?? "", message: infoMessage?.message ?? "", confirmLabel: "OK", icon: "information-outline", rotation: 0, onConfirm: () => setInfoMessage(null) })
|
|
449
|
-
] }) });
|
|
450
|
-
}
|
|
451
|
-
var styles8 = StyleSheet8.create({
|
|
452
|
-
content: {
|
|
453
|
-
gap: 24,
|
|
454
|
-
paddingVertical: 20
|
|
455
|
-
},
|
|
456
|
-
// Caps the card so it never grows past the screen — without this the dialog just keeps
|
|
457
|
-
// growing to fit its content and the overflow gets clipped by the screen edge, which is
|
|
458
|
-
// what happened in landscape where there's less height to work with. Dialog.ScrollArea +
|
|
459
|
-
// ScrollView below then take over and let the content scroll within that bound.
|
|
460
|
-
dialog: {
|
|
461
|
-
maxHeight: "90%"
|
|
462
|
-
},
|
|
463
|
-
flexShrink: {
|
|
464
|
-
flexShrink: 1
|
|
465
|
-
},
|
|
466
|
-
iconBadge: {
|
|
467
|
-
alignItems: "center",
|
|
468
|
-
borderRadius: 10,
|
|
469
|
-
height: 36,
|
|
470
|
-
justifyContent: "center",
|
|
471
|
-
width: 36
|
|
472
|
-
},
|
|
473
|
-
scrollArea: {
|
|
474
|
-
marginBottom: 0
|
|
475
|
-
},
|
|
476
|
-
section: {
|
|
477
|
-
gap: 12
|
|
478
|
-
},
|
|
479
|
-
sectionLabel: {
|
|
480
|
-
letterSpacing: 2
|
|
481
|
-
},
|
|
482
|
-
// Used to carry marginHorizontal: -12 + paddingHorizontal: 12 (netting to the same inset as
|
|
483
|
-
// APPEARANCE/SOUND & HAPTICS below, while giving the ripple/hover highlight room to breathe
|
|
484
|
-
// into the dialog's own gutter beyond that). Removed: this dialog scrolls (Dialog.ScrollArea +
|
|
485
|
-
// ScrollView above), and on web a ScrollView's own content wrapper clips to its own bounds
|
|
486
|
-
// regardless of a child's negative margin - confirmed live, the icon's own rounded-square badge
|
|
487
|
-
// was getting its left edge sliced off by exactly that overhang. No horizontal inset of its own
|
|
488
|
-
// now - it relies purely on the same dialog padding APPEARANCE/every other row already does,
|
|
489
|
-
// which can never overflow because it's never negative.
|
|
490
|
-
toggleButton: {
|
|
491
|
-
borderRadius: 12,
|
|
492
|
-
overflow: "hidden",
|
|
493
|
-
paddingVertical: 10
|
|
494
|
-
},
|
|
495
|
-
// Same shape as toggleButton, `flex: 1` instead of its own width - two of these side by side in
|
|
496
|
-
// toggleRow share the row's own edge alignment instead of each carrying their own.
|
|
497
|
-
toggleButtonInRow: {
|
|
498
|
-
borderRadius: 12,
|
|
499
|
-
flex: 1,
|
|
500
|
-
overflow: "hidden",
|
|
501
|
-
paddingVertical: 10
|
|
502
|
-
},
|
|
503
|
-
toggleContent: {
|
|
504
|
-
alignItems: "center",
|
|
505
|
-
flexDirection: "row",
|
|
506
|
-
gap: 12
|
|
507
|
-
},
|
|
508
|
-
toggleGroup: {
|
|
509
|
-
gap: 4
|
|
510
|
-
},
|
|
511
|
-
// No horizontal margin of its own now either - see toggleButton's identical note.
|
|
512
|
-
toggleRow: {
|
|
513
|
-
flexDirection: "row",
|
|
514
|
-
gap: 4
|
|
515
|
-
}
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
// src/BaseStatsScreen.tsx
|
|
519
|
-
import { useAutoPaperTheme as useAutoPaperTheme8 } from "@rific/auto-paper";
|
|
520
|
-
import { Button as Button3, IconButton } from "@rific/feedback-press";
|
|
521
|
-
import { useRotation as useRotation3 } from "@tastic/core";
|
|
522
|
-
import { useState as useState3 } from "react";
|
|
523
|
-
import { ScrollView as ScrollView2, StyleSheet as StyleSheet9, View as View8 } from "react-native";
|
|
524
|
-
import { Text as Text8 } from "react-native-paper";
|
|
525
|
-
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
526
|
-
function BaseStatsScreen({ rotation: rotationOverride, title = "Achievements", titleVariant = "headlineSmall", onBack, insets, onReset, resetLabel = "Reset All Stats", resetConfirmTitle = "Reset Everything?", resetConfirmBody = "This permanently erases all stats and achievements. This cannot be undone.", fg: fgOverride, bg: bgOverride, children }) {
|
|
527
|
-
const { dark, colors } = useAutoPaperTheme8();
|
|
528
|
-
const ambientRotation = useRotation3();
|
|
529
|
-
const rotation = rotationOverride ?? ambientRotation;
|
|
530
|
-
const [confirmResetVisible, setConfirmResetVisible] = useState3(false);
|
|
531
|
-
const showReset = !!onReset;
|
|
532
|
-
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
533
|
-
const bg = bgOverride ?? (dark ? "#000000" : "#FFFFFF");
|
|
534
|
-
return /* @__PURE__ */ jsxs9(View8, { style: [styles9.container, { backgroundColor: bg }], children: [
|
|
535
|
-
/* @__PURE__ */ jsxs9(View8, { style: [styles9.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
|
|
536
|
-
/* @__PURE__ */ jsx10(IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
|
|
537
|
-
/* @__PURE__ */ jsx10(Text8, { variant: titleVariant, style: [styles9.title, { color: fg }], children: title })
|
|
538
|
-
] }),
|
|
539
|
-
/* @__PURE__ */ jsxs9(ScrollView2, { style: styles9.scrollView, contentContainerStyle: [styles9.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
|
|
540
|
-
children,
|
|
541
|
-
showReset && /* @__PURE__ */ jsx10(Button3, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.danger, textColor: colors.onDanger, style: styles9.resetButton, children: resetLabel })
|
|
542
|
-
] }),
|
|
543
|
-
/* @__PURE__ */ jsx10(
|
|
544
|
-
ConfirmDialog,
|
|
545
|
-
{
|
|
546
|
-
visible: confirmResetVisible,
|
|
547
|
-
title: resetConfirmTitle,
|
|
548
|
-
message: resetConfirmBody,
|
|
549
|
-
confirmLabel: "Reset",
|
|
550
|
-
cancelLabel: "Cancel",
|
|
551
|
-
icon: "alert-outline",
|
|
552
|
-
destructive: true,
|
|
553
|
-
rotation,
|
|
554
|
-
onCancel: () => setConfirmResetVisible(false),
|
|
555
|
-
onConfirm: () => {
|
|
556
|
-
onReset?.();
|
|
557
|
-
setConfirmResetVisible(false);
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
)
|
|
561
|
-
] });
|
|
562
|
-
}
|
|
563
|
-
var styles9 = StyleSheet9.create({
|
|
564
|
-
container: {
|
|
565
|
-
flex: 1
|
|
566
|
-
},
|
|
567
|
-
content: {
|
|
568
|
-
gap: 12,
|
|
569
|
-
paddingHorizontal: 20
|
|
570
|
-
},
|
|
571
|
-
header: {
|
|
572
|
-
alignItems: "center",
|
|
573
|
-
flexDirection: "row",
|
|
574
|
-
gap: 4,
|
|
575
|
-
paddingBottom: 8
|
|
576
|
-
},
|
|
577
|
-
resetButton: {
|
|
578
|
-
marginTop: 16
|
|
579
|
-
},
|
|
580
|
-
// Bounds the ScrollView to the space `container`'s flex:1 actually gives it — without this, a
|
|
581
|
-
// ScrollView with only a contentContainerStyle isn't reliably height-constrained on native (it
|
|
582
|
-
// can render at its full unclipped content height instead of the screen's), which leaves the
|
|
583
|
-
// header's back button touch target unreliable underneath it.
|
|
584
|
-
scrollView: {
|
|
585
|
-
flex: 1
|
|
586
|
-
},
|
|
587
|
-
title: {
|
|
588
|
-
flexShrink: 1,
|
|
589
|
-
fontWeight: "bold"
|
|
590
|
-
}
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
// src/ContentGutter.tsx
|
|
594
|
-
import { computeContentBounds, useRotatedWindowDimensions } from "@tastic/core";
|
|
595
|
-
import { StyleSheet as StyleSheet10, View as View9 } from "react-native";
|
|
596
|
-
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
597
|
-
function ContentGutter({ maxContentWidth, leftGutter, rightGutter, style, children }) {
|
|
598
|
-
const { width: windowWidth } = useRotatedWindowDimensions();
|
|
599
|
-
const { contentWidth, gutterWidth } = computeContentBounds(windowWidth, maxContentWidth ?? Infinity);
|
|
600
|
-
return /* @__PURE__ */ jsxs10(View9, { style: [styles10.row, style], children: [
|
|
601
|
-
/* @__PURE__ */ jsx11(View9, { style: [styles10.gutter, { width: gutterWidth }], children: leftGutter }),
|
|
602
|
-
/* @__PURE__ */ jsx11(View9, { style: { width: contentWidth }, children }),
|
|
603
|
-
/* @__PURE__ */ jsx11(View9, { style: [styles10.gutter, { width: gutterWidth }], children: rightGutter })
|
|
604
|
-
] });
|
|
605
|
-
}
|
|
606
|
-
var styles10 = StyleSheet10.create({
|
|
607
|
-
// overflow:'hidden' keeps an oversized leftGutter/rightGutter from bleeding into the content
|
|
608
|
-
// column instead of clipping cleanly at its own real width.
|
|
609
|
-
gutter: {
|
|
610
|
-
overflow: "hidden"
|
|
611
|
-
},
|
|
612
|
-
row: {
|
|
613
|
-
flexDirection: "row"
|
|
614
|
-
}
|
|
615
|
-
});
|
|
616
|
-
|
|
617
72
|
// src/ControlSchemePicker.tsx
|
|
618
|
-
import { useIsTouchPrimaryDevice
|
|
619
|
-
import { Platform
|
|
73
|
+
import { useIsTouchPrimaryDevice } from "@tastic/core";
|
|
74
|
+
import { Platform } from "react-native";
|
|
620
75
|
|
|
621
76
|
// src/SectionedDropdown.tsx
|
|
622
77
|
import { getBlendedColor, getColorRoles } from "@rific/auto-paper";
|
|
623
|
-
import { IconButton
|
|
624
|
-
import { useRotation
|
|
625
|
-
import { ScrollView
|
|
626
|
-
import { Icon as
|
|
78
|
+
import { IconButton, TouchableRipple } from "@rific/feedback-press";
|
|
79
|
+
import { useRotation } from "@tastic/core";
|
|
80
|
+
import { ScrollView, StyleSheet as StyleSheet3, View as View4 } from "react-native";
|
|
81
|
+
import { Icon as Icon2, Text as Text2 } from "react-native-paper";
|
|
627
82
|
|
|
628
83
|
// src/PopoverBody.tsx
|
|
629
|
-
import { StyleSheet as
|
|
630
|
-
import { Fragment
|
|
84
|
+
import { StyleSheet as StyleSheet2, View as View2 } from "react-native";
|
|
85
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
631
86
|
var CARET_SIZE = 7;
|
|
632
87
|
var CARET_RING_OFFSET = 2;
|
|
633
88
|
var CARET_DIP = 1;
|
|
634
89
|
function PopoverBody({ visible, children, align = "center", verticalAlign = "below", caretColor, caretBorderColor, caretBorderWidth = 1, triggerSize = 0 }) {
|
|
635
90
|
if (!visible) return null;
|
|
636
91
|
const above = verticalAlign === "above";
|
|
637
|
-
const alignStyle = align === "left" ?
|
|
638
|
-
const verticalStyle = above ?
|
|
92
|
+
const alignStyle = align === "left" ? styles2.contentLeft : align === "right" ? styles2.contentRight : styles2.contentCenter;
|
|
93
|
+
const verticalStyle = above ? styles2.contentAbove : styles2.contentBelow;
|
|
639
94
|
const ringSize = CARET_SIZE + caretBorderWidth;
|
|
640
95
|
const caretOffset = (halfFootprint) => align === "right" ? { right: triggerSize / 2 - halfFootprint } : { left: triggerSize / 2 - halfFootprint };
|
|
641
96
|
const caretFill = above ? "borderTopColor" : "borderBottomColor";
|
|
642
97
|
const caretWidthProp = above ? "borderTopWidth" : "borderBottomWidth";
|
|
643
98
|
const caretEdge = (size) => above ? { bottom: -size + CARET_DIP } : { top: -size + CARET_DIP };
|
|
644
|
-
return /* @__PURE__ */
|
|
99
|
+
return /* @__PURE__ */ jsxs2(View2, { style: [styles2.content, alignStyle, verticalStyle], children: [
|
|
645
100
|
children,
|
|
646
|
-
caretColor && /* @__PURE__ */
|
|
647
|
-
/* @__PURE__ */
|
|
648
|
-
/* @__PURE__ */
|
|
101
|
+
caretColor && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
102
|
+
/* @__PURE__ */ jsx2(View2, { style: [styles2.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
|
|
103
|
+
/* @__PURE__ */ jsx2(View2, { style: [styles2.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
|
|
649
104
|
] })
|
|
650
105
|
] });
|
|
651
106
|
}
|
|
652
|
-
var
|
|
107
|
+
var styles2 = StyleSheet2.create({
|
|
653
108
|
caret: {
|
|
654
109
|
borderLeftColor: "transparent",
|
|
655
110
|
borderRightColor: "transparent",
|
|
656
111
|
height: 0,
|
|
657
112
|
position: "absolute",
|
|
658
113
|
width: 0,
|
|
659
|
-
// Explicit, not left to DOM order
|
|
114
|
+
// Explicit, not left to DOM order - the box it dips into is itself a position:'relative' view,
|
|
660
115
|
// which React Native Web always gives an implicit zIndex:0 (see PopoverBody's stacking-context
|
|
661
116
|
// notes elsewhere in this codebase), so painting on top needs a real zIndex to beat that
|
|
662
117
|
// reliably rather than relying on being the later sibling.
|
|
@@ -675,7 +130,7 @@ var styles11 = StyleSheet11.create({
|
|
|
675
130
|
top: "100%"
|
|
676
131
|
},
|
|
677
132
|
// Stretched to the anchor's own (trigger) width, with alignItems centering the actual (usually
|
|
678
|
-
// much wider) popover box inside via plain flexbox
|
|
133
|
+
// much wider) popover box inside via plain flexbox - not `left:'50%'` plus a percentage
|
|
679
134
|
// `transform`, which depends on native resolving the box's own auto width before applying the
|
|
680
135
|
// transform and has proven unreliable there, visibly shifting the box off-center.
|
|
681
136
|
contentCenter: {
|
|
@@ -692,15 +147,15 @@ var styles11 = StyleSheet11.create({
|
|
|
692
147
|
});
|
|
693
148
|
|
|
694
149
|
// src/TriggerGaugeHost.tsx
|
|
695
|
-
import { jsx as
|
|
150
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
696
151
|
var getTriggerGauge = () => import("./TriggerGauge-2OJOOF6I.mjs").then((mod) => ({ default: mod.TriggerGauge }));
|
|
697
152
|
function TriggerGaugeHost(props) {
|
|
698
|
-
return /* @__PURE__ */
|
|
153
|
+
return /* @__PURE__ */ jsx3(SkiaGate, { getComponent: getTriggerGauge, componentProps: props });
|
|
699
154
|
}
|
|
700
155
|
|
|
701
156
|
// src/useAutoAlign.ts
|
|
702
|
-
import { useCallback
|
|
703
|
-
import { useWindowDimensions
|
|
157
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
158
|
+
import { useWindowDimensions } from "react-native";
|
|
704
159
|
var EDGE_MARGIN = 12;
|
|
705
160
|
function rotateRect(x, y, width, height, rotation, windowWidth, windowHeight) {
|
|
706
161
|
if (rotation === 0) return { x, y, width, height };
|
|
@@ -712,18 +167,18 @@ function rotateRect(x, y, width, height, rotation, windowWidth, windowHeight) {
|
|
|
712
167
|
return { x: cx + newDx - newWidth / 2, y: cy + newDy - newHeight / 2, width: newWidth, height: newHeight };
|
|
713
168
|
}
|
|
714
169
|
function useAutoAlign(open, contentWidth, contentHeight, rotation = 0) {
|
|
715
|
-
const triggerRef =
|
|
716
|
-
const { width: windowWidth, height: windowHeight } =
|
|
717
|
-
const [align, setAlign] =
|
|
718
|
-
const [verticalAlign, setVerticalAlign] =
|
|
719
|
-
const [maxHeight, setMaxHeight] =
|
|
720
|
-
const [measured, setMeasured] =
|
|
721
|
-
const [prevOpen, setPrevOpen] =
|
|
170
|
+
const triggerRef = useRef(null);
|
|
171
|
+
const { width: windowWidth, height: windowHeight } = useWindowDimensions();
|
|
172
|
+
const [align, setAlign] = useState("center");
|
|
173
|
+
const [verticalAlign, setVerticalAlign] = useState("below");
|
|
174
|
+
const [maxHeight, setMaxHeight] = useState(contentHeight);
|
|
175
|
+
const [measured, setMeasured] = useState(false);
|
|
176
|
+
const [prevOpen, setPrevOpen] = useState(open);
|
|
722
177
|
if (open !== prevOpen) {
|
|
723
178
|
setPrevOpen(open);
|
|
724
179
|
if (open) setMeasured(false);
|
|
725
180
|
}
|
|
726
|
-
const measure =
|
|
181
|
+
const measure = useCallback(() => {
|
|
727
182
|
triggerRef.current?.measureInWindow((rawX, rawY, rawWidth, rawHeight) => {
|
|
728
183
|
const { x, y, width: triggerWidth, height: triggerHeight } = rotateRect(rawX, rawY, rawWidth, rawHeight, rotation, windowWidth, windowHeight);
|
|
729
184
|
const centerX = x + triggerWidth / 2;
|
|
@@ -747,7 +202,7 @@ function useAutoAlign(open, contentWidth, contentHeight, rotation = 0) {
|
|
|
747
202
|
}
|
|
748
203
|
|
|
749
204
|
// src/SectionedDropdown.tsx
|
|
750
|
-
import { Fragment as
|
|
205
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
751
206
|
var MENU_BORDER_WIDTH = 1;
|
|
752
207
|
var TRIGGER_SIZE = 44;
|
|
753
208
|
var MENU_MIN_WIDTH = 200;
|
|
@@ -759,7 +214,7 @@ var MENU_ITEM_HEIGHT_WITH_DESCRIPTION = 58;
|
|
|
759
214
|
var MENU_DIVIDER_HEIGHT = 9;
|
|
760
215
|
var MENU_VERTICAL_PADDING = 12;
|
|
761
216
|
function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accentColor, mutedColor, onAccentColor, dark, align: forcedAlign, alignOverride, rotation: rotationOverride, autoDismiss = true, labelFontFamily = MONO_FONT, allClearLabels = { all: "All", clear: "Clear" } }) {
|
|
762
|
-
const ambientRotation =
|
|
217
|
+
const ambientRotation = useRotation();
|
|
763
218
|
const rotation = rotationOverride ?? ambientRotation;
|
|
764
219
|
const menuBg = dark ? "#000000" : "#FFFFFF";
|
|
765
220
|
const menuBorderColor = getBlendedColor(mutedColor, menuBg, 0.5);
|
|
@@ -797,17 +252,17 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
|
|
|
797
252
|
}
|
|
798
253
|
gaugeOffset += section.options.length;
|
|
799
254
|
}
|
|
800
|
-
const anchorStyle = [
|
|
255
|
+
const anchorStyle = [styles3.anchor, open && styles3.anchorOpen];
|
|
801
256
|
const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
|
|
802
257
|
const dividerStyle = { backgroundColor: menuBorderColor };
|
|
803
|
-
return /* @__PURE__ */
|
|
804
|
-
/* @__PURE__ */
|
|
805
|
-
/* @__PURE__ */
|
|
806
|
-
/* @__PURE__ */
|
|
258
|
+
return /* @__PURE__ */ jsxs3(View4, { style: anchorStyle, children: [
|
|
259
|
+
/* @__PURE__ */ jsxs3(View4, { ref: triggerRef, collapsable: false, style: styles3.triggerBox, children: [
|
|
260
|
+
/* @__PURE__ */ jsx4(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
|
|
261
|
+
/* @__PURE__ */ jsx4(IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
|
|
807
262
|
] }),
|
|
808
|
-
/* @__PURE__ */
|
|
809
|
-
sectionIndex > 0 && /* @__PURE__ */
|
|
810
|
-
section.kind === "single" ? /* @__PURE__ */
|
|
263
|
+
/* @__PURE__ */ jsx4(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ jsx4(ScrollView, { style: [styles3.menu, menuStyle], contentContainerStyle: styles3.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs3(View4, { children: [
|
|
264
|
+
sectionIndex > 0 && /* @__PURE__ */ jsx4(View4, { style: [styles3.divider, dividerStyle] }),
|
|
265
|
+
section.kind === "single" ? /* @__PURE__ */ jsx4(
|
|
811
266
|
SingleSection,
|
|
812
267
|
{
|
|
813
268
|
section,
|
|
@@ -820,7 +275,7 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
|
|
|
820
275
|
if (autoDismiss) host.close();
|
|
821
276
|
}
|
|
822
277
|
}
|
|
823
|
-
) : /* @__PURE__ */
|
|
278
|
+
) : /* @__PURE__ */ jsx4(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
|
|
824
279
|
] }, section.id)) }) })
|
|
825
280
|
] });
|
|
826
281
|
}
|
|
@@ -832,18 +287,18 @@ function SingleSection({
|
|
|
832
287
|
labelFontFamily,
|
|
833
288
|
onSelect
|
|
834
289
|
}) {
|
|
835
|
-
return /* @__PURE__ */
|
|
290
|
+
return /* @__PURE__ */ jsx4(Fragment2, { children: section.options.map((option) => {
|
|
836
291
|
const selected = option.value === section.value;
|
|
837
292
|
const taken = !selected && option.value === section.takenValue;
|
|
838
293
|
const rowColor = selected ? onAccentColor : mutedColor;
|
|
839
|
-
const itemStyle = [
|
|
294
|
+
const itemStyle = [styles3.item, selected && { backgroundColor: accentColor }, taken && styles3.itemTaken];
|
|
840
295
|
const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
|
|
841
296
|
const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
|
|
842
|
-
return /* @__PURE__ */
|
|
843
|
-
option.icon && /* @__PURE__ */
|
|
844
|
-
/* @__PURE__ */
|
|
845
|
-
/* @__PURE__ */
|
|
846
|
-
option.description && /* @__PURE__ */
|
|
297
|
+
return /* @__PURE__ */ jsx4(TouchableRipple, { disabled: taken, onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ jsxs3(View4, { style: styles3.itemRow, children: [
|
|
298
|
+
option.icon && /* @__PURE__ */ jsx4(Icon2, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
|
|
299
|
+
/* @__PURE__ */ jsxs3(View4, { style: styles3.itemLabelColumn, children: [
|
|
300
|
+
/* @__PURE__ */ jsx4(Text2, { variant: "labelLarge", style: labelStyle, children: option.label }),
|
|
301
|
+
option.description && /* @__PURE__ */ jsx4(Text2, { variant: "labelSmall", style: descriptionStyle, children: option.description })
|
|
847
302
|
] })
|
|
848
303
|
] }) }, option.value);
|
|
849
304
|
}) });
|
|
@@ -857,23 +312,23 @@ function MultiSection({
|
|
|
857
312
|
allClearLabels
|
|
858
313
|
}) {
|
|
859
314
|
const allSelected = section.value.length === section.options.length;
|
|
860
|
-
return /* @__PURE__ */
|
|
315
|
+
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
861
316
|
section.options.map((option, i) => {
|
|
862
317
|
const selected = section.value.includes(option.value);
|
|
863
318
|
const rowColor = selected ? onAccentColor : mutedColor;
|
|
864
319
|
const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value);
|
|
865
320
|
const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
|
|
866
|
-
const itemStyle = [
|
|
321
|
+
const itemStyle = [styles3.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
|
|
867
322
|
const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
|
|
868
|
-
return /* @__PURE__ */
|
|
869
|
-
option.icon && /* @__PURE__ */
|
|
870
|
-
/* @__PURE__ */
|
|
323
|
+
return /* @__PURE__ */ jsx4(TouchableRipple, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ jsxs3(View4, { style: styles3.itemRow, children: [
|
|
324
|
+
option.icon && /* @__PURE__ */ jsx4(Icon2, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
|
|
325
|
+
/* @__PURE__ */ jsx4(Text2, { variant: "labelLarge", style: [styles3.itemLabelFlex, labelStyle], children: option.label })
|
|
871
326
|
] }) }, option.value);
|
|
872
327
|
}),
|
|
873
|
-
section.allClear && /* @__PURE__ */
|
|
328
|
+
section.allClear && /* @__PURE__ */ jsx4(TouchableRipple, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles3.item, children: /* @__PURE__ */ jsx4(Text2, { variant: "labelLarge", style: [styles3.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
|
|
874
329
|
] });
|
|
875
330
|
}
|
|
876
|
-
var
|
|
331
|
+
var styles3 = StyleSheet3.create({
|
|
877
332
|
allClearLabel: {
|
|
878
333
|
fontWeight: "bold",
|
|
879
334
|
textAlign: "center"
|
|
@@ -882,7 +337,7 @@ var styles12 = StyleSheet12.create({
|
|
|
882
337
|
position: "relative"
|
|
883
338
|
},
|
|
884
339
|
// React Native Web gives every position:'relative' view an explicit zIndex (0, not 'auto'),
|
|
885
|
-
// which makes each one its own stacking context
|
|
340
|
+
// which makes each one its own stacking context - so a zIndex set only on the popover content
|
|
886
341
|
// below only wins comparisons within this anchor's own box, not against this anchor's own later
|
|
887
342
|
// siblings (e.g. a Ready button), which are otherwise painted on top by DOM-order tiebreak.
|
|
888
343
|
// Elevating the anchor itself, only while its popover is open, fixes that at the right level.
|
|
@@ -890,7 +345,7 @@ var styles12 = StyleSheet12.create({
|
|
|
890
345
|
zIndex: 100
|
|
891
346
|
},
|
|
892
347
|
divider: {
|
|
893
|
-
height:
|
|
348
|
+
height: StyleSheet3.hairlineWidth,
|
|
894
349
|
marginVertical: 4
|
|
895
350
|
},
|
|
896
351
|
item: {
|
|
@@ -909,7 +364,7 @@ var styles12 = StyleSheet12.create({
|
|
|
909
364
|
flexDirection: "row",
|
|
910
365
|
gap: 8
|
|
911
366
|
},
|
|
912
|
-
// Same opacity as ProfilePicker's rowDisabled / InlineColorPicker's swatchTaken
|
|
367
|
+
// Same opacity as ProfilePicker's rowDisabled / InlineColorPicker's swatchTaken - the fleet's
|
|
913
368
|
// established "shown, not removed" treatment for another seat's own current pick.
|
|
914
369
|
itemTaken: {
|
|
915
370
|
opacity: 0.35
|
|
@@ -925,7 +380,7 @@ var styles12 = StyleSheet12.create({
|
|
|
925
380
|
shadowOpacity: 0.3,
|
|
926
381
|
shadowRadius: 8
|
|
927
382
|
},
|
|
928
|
-
// Separate from `menu` itself
|
|
383
|
+
// Separate from `menu` itself - a ScrollView's own style sets the scrolling frame's bounds
|
|
929
384
|
// (including the maxHeight that actually caps it), while padding/gap belong to its
|
|
930
385
|
// contentContainerStyle, which sizes to the *unclamped* content instead of the frame.
|
|
931
386
|
menuContent: {
|
|
@@ -943,49 +398,30 @@ var styles12 = StyleSheet12.create({
|
|
|
943
398
|
});
|
|
944
399
|
|
|
945
400
|
// src/ControlSchemePicker.tsx
|
|
946
|
-
import { jsx as
|
|
401
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
947
402
|
function ControlSchemePicker({ id, host, value, onChange, options, takenValue, accentColor, mutedColor, onAccentColor, dark, align, alignOverride, rotation, labelFontFamily }) {
|
|
948
|
-
const isTouchPrimary =
|
|
949
|
-
if (
|
|
950
|
-
return /* @__PURE__ */
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
// src/CornerActionButtons.tsx
|
|
954
|
-
import { IconButton as IconButton3 } from "@rific/feedback-press";
|
|
955
|
-
import { StyleSheet as StyleSheet13 } from "react-native";
|
|
956
|
-
import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
957
|
-
function CornerActionButtons({ onBack, onSettings, fg, insets, backIcon = "arrow-left", backLabel = "Back" }) {
|
|
958
|
-
return /* @__PURE__ */ jsxs13(Fragment4, { children: [
|
|
959
|
-
/* @__PURE__ */ jsx16(IconButton3, { icon: backIcon, iconColor: fg, size: 24, style: [styles13.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: backLabel }),
|
|
960
|
-
/* @__PURE__ */ jsx16(IconButton3, { icon: "cog", iconColor: fg, size: 24, style: [styles13.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
|
|
961
|
-
] });
|
|
403
|
+
const isTouchPrimary = useIsTouchPrimaryDevice();
|
|
404
|
+
if (Platform.OS !== "web" || isTouchPrimary) return null;
|
|
405
|
+
return /* @__PURE__ */ jsx5(SectionedDropdown, { id, host, icon: "keyboard-outline", accessibilityLabel: "Control scheme", sections: [{ kind: "single", id: "controls", options, value, onChange, takenValue }], accentColor, mutedColor, onAccentColor, dark, align, alignOverride, rotation, labelFontFamily });
|
|
962
406
|
}
|
|
963
|
-
var styles13 = StyleSheet13.create({
|
|
964
|
-
back: {
|
|
965
|
-
position: "absolute"
|
|
966
|
-
},
|
|
967
|
-
topRight: {
|
|
968
|
-
position: "absolute"
|
|
969
|
-
}
|
|
970
|
-
});
|
|
971
407
|
|
|
972
408
|
// src/CornerStatusBadges.tsx
|
|
973
|
-
import { StyleSheet as
|
|
974
|
-
import { Icon as
|
|
975
|
-
import { Fragment as
|
|
409
|
+
import { StyleSheet as StyleSheet4, View as View5 } from "react-native";
|
|
410
|
+
import { Icon as Icon3 } from "react-native-paper";
|
|
411
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
976
412
|
var BADGE_SIZE = 30;
|
|
977
413
|
function StatusBadge({ icon }) {
|
|
978
|
-
return /* @__PURE__ */
|
|
414
|
+
return /* @__PURE__ */ jsx6(View5, { style: styles4.badge, children: icon && /* @__PURE__ */ jsx6(Icon3, { source: icon, size: 16, color: "rgba(255,255,255,0.85)" }) });
|
|
979
415
|
}
|
|
980
416
|
function CornerStatusBadges({ orientationMode, p1OnRight, p1, p2 }) {
|
|
981
417
|
const p1OnRightSide = orientationMode === "sideBySide" && p1OnRight;
|
|
982
418
|
const p2OnLeftSide = orientationMode === "sideBySide" && !p1OnRight;
|
|
983
|
-
return /* @__PURE__ */
|
|
984
|
-
p1 && /* @__PURE__ */
|
|
985
|
-
p2 && /* @__PURE__ */
|
|
419
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
420
|
+
p1 && /* @__PURE__ */ jsx6(View5, { pointerEvents: "none", style: p1OnRightSide ? styles4.bottomRight : styles4.bottomLeft, children: /* @__PURE__ */ jsx6(StatusBadge, { icon: p1.icon }) }),
|
|
421
|
+
p2 && /* @__PURE__ */ jsx6(View5, { pointerEvents: "none", style: p2OnLeftSide ? styles4.topLeft : styles4.topRight, children: /* @__PURE__ */ jsx6(StatusBadge, { icon: p2.icon }) })
|
|
986
422
|
] });
|
|
987
423
|
}
|
|
988
|
-
var
|
|
424
|
+
var styles4 = StyleSheet4.create({
|
|
989
425
|
badge: {
|
|
990
426
|
alignItems: "center",
|
|
991
427
|
backgroundColor: "rgba(0,0,0,0.28)",
|
|
@@ -1005,11 +441,11 @@ var styles14 = StyleSheet14.create({
|
|
|
1005
441
|
|
|
1006
442
|
// src/InlineColorPicker.tsx
|
|
1007
443
|
import { defaultColors, getContrastColor as getContrastColor2 } from "@rific/auto-paper";
|
|
1008
|
-
import { TouchableRipple as
|
|
1009
|
-
import { clamp, useRotation as
|
|
1010
|
-
import { ScrollView as
|
|
1011
|
-
import { Icon as
|
|
1012
|
-
import { jsx as
|
|
444
|
+
import { TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
|
|
445
|
+
import { clamp, useRotation as useRotation2 } from "@tastic/core";
|
|
446
|
+
import { ScrollView as ScrollView2, StyleSheet as StyleSheet5, useWindowDimensions as useWindowDimensions2, View as View6 } from "react-native";
|
|
447
|
+
import { Icon as Icon4, Text as Text3 } from "react-native-paper";
|
|
448
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1013
449
|
var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
|
|
1014
450
|
var DEFAULT_SIZE = 48;
|
|
1015
451
|
var SWATCH_SIZE = 28;
|
|
@@ -1031,10 +467,10 @@ function getInlineColorPickerContentSize(swatchCount, windowWidth, columns) {
|
|
|
1031
467
|
return { width, height };
|
|
1032
468
|
}
|
|
1033
469
|
function InlineColorPicker({ id, host, value, onChange, previewValue, swatches = defaultColors, takenValue, allowSwapTaken, dark, align: forcedAlign, alignOverride, rotation: rotationOverride, icon = "palette", tag, labelFontFamily = MONO_FONT, autoDismiss = true, columns, size = DEFAULT_SIZE }) {
|
|
1034
|
-
const ambientRotation =
|
|
470
|
+
const ambientRotation = useRotation2();
|
|
1035
471
|
const rotation = rotationOverride ?? ambientRotation;
|
|
1036
472
|
const menuBg = dark ? "#000000" : "#FFFFFF";
|
|
1037
|
-
const { width: windowWidth } =
|
|
473
|
+
const { width: windowWidth } = useWindowDimensions2();
|
|
1038
474
|
const { width: swatchesWidth, height: swatchesHeight } = getInlineColorPickerContentSize(swatches.length, windowWidth, columns);
|
|
1039
475
|
const open = host.openId === id;
|
|
1040
476
|
const auto = useAutoAlign(open, swatchesWidth, swatchesHeight, rotation);
|
|
@@ -1045,15 +481,15 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
|
|
|
1045
481
|
const displayValue = displayFor(value);
|
|
1046
482
|
const contrastColor = getContrastColor2(displayValue);
|
|
1047
483
|
const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : 0.4);
|
|
1048
|
-
return /* @__PURE__ */
|
|
1049
|
-
/* @__PURE__ */
|
|
1050
|
-
/* @__PURE__ */
|
|
484
|
+
return /* @__PURE__ */ jsxs5(View6, { style: [styles5.anchor, open && styles5.anchorOpen], children: [
|
|
485
|
+
/* @__PURE__ */ jsx7(TouchableRipple2, { onPress: () => host.toggle(id), borderless: true, style: [styles5.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ jsx7(View6, { ref: triggerRef, collapsable: false, style: styles5.triggerMeasure, children: tag ? /* @__PURE__ */ jsx7(Text3, { style: [styles5.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ jsx7(Icon4, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
|
|
486
|
+
/* @__PURE__ */ jsx7(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: displayValue, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ jsx7(ScrollView2, { style: [styles5.swatches, { backgroundColor: menuBg, borderColor: displayValue, width: swatchesWidth, maxHeight }], contentContainerStyle: styles5.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
|
|
1051
487
|
const selected = swatch.value.toLowerCase() === value.toLowerCase();
|
|
1052
488
|
const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
|
|
1053
489
|
const swappable = taken && allowSwapTaken;
|
|
1054
490
|
const swatchDisplay = displayFor(swatch.value);
|
|
1055
|
-
return /* @__PURE__ */
|
|
1056
|
-
|
|
491
|
+
return /* @__PURE__ */ jsx7(
|
|
492
|
+
TouchableRipple2,
|
|
1057
493
|
{
|
|
1058
494
|
disabled: taken && !swappable,
|
|
1059
495
|
onPress: () => {
|
|
@@ -1061,19 +497,19 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
|
|
|
1061
497
|
if (autoDismiss) host.close();
|
|
1062
498
|
},
|
|
1063
499
|
borderless: true,
|
|
1064
|
-
style: [
|
|
1065
|
-
children: selected ? /* @__PURE__ */
|
|
500
|
+
style: [styles5.swatch, { backgroundColor: swatchDisplay }, selected && styles5.swatchSelected, taken && !swappable && styles5.swatchTaken],
|
|
501
|
+
children: selected ? /* @__PURE__ */ jsx7(Icon4, { source: "check", size: 16, color: getContrastColor2(swatchDisplay) }) : swappable ? /* @__PURE__ */ jsx7(Icon4, { source: "swap-horizontal", size: 16, color: getContrastColor2(swatchDisplay) }) : taken ? /* @__PURE__ */ jsx7(Icon4, { source: "close", size: 16, color: getContrastColor2(swatchDisplay) }) : /* @__PURE__ */ jsx7(View6, {})
|
|
1066
502
|
},
|
|
1067
503
|
swatch.value
|
|
1068
504
|
);
|
|
1069
505
|
}) }) })
|
|
1070
506
|
] });
|
|
1071
507
|
}
|
|
1072
|
-
var
|
|
508
|
+
var styles5 = StyleSheet5.create({
|
|
1073
509
|
anchor: {
|
|
1074
510
|
position: "relative"
|
|
1075
511
|
},
|
|
1076
|
-
// See SectionedDropdown's identical comment
|
|
512
|
+
// See SectionedDropdown's identical comment - React Native Web gives every position:'relative' view
|
|
1077
513
|
// its own stacking context, so the elevation has to live on the anchor itself, not just the
|
|
1078
514
|
// popover content nested inside it, to correctly paint above this anchor's own later siblings.
|
|
1079
515
|
anchorOpen: {
|
|
@@ -1101,12 +537,12 @@ var styles15 = StyleSheet15.create({
|
|
|
1101
537
|
shadowOffset: { height: 2, width: 0 },
|
|
1102
538
|
shadowOpacity: 0.3,
|
|
1103
539
|
shadowRadius: 8
|
|
1104
|
-
// width is set inline above
|
|
540
|
+
// width is set inline above - border-box sizing means it has to include the border too
|
|
1105
541
|
// (2*SWATCHES_BORDER_WIDTH), not just padding+content, or exactly enough room goes missing that
|
|
1106
542
|
// the last column silently wraps to a new row. Anything wider than the exact sum just shows as
|
|
1107
543
|
// dead space on the right edge of each row instead.
|
|
1108
544
|
},
|
|
1109
|
-
// Separate from `swatches` itself
|
|
545
|
+
// Separate from `swatches` itself - see SectionedDropdown's identical menu/menuContent split.
|
|
1110
546
|
// The scroll frame's own bounds (including maxHeight) live on the ScrollView; the actual grid
|
|
1111
547
|
// wrapping belongs to its contentContainerStyle, which sizes to the unclamped content instead.
|
|
1112
548
|
swatchesContent: {
|
|
@@ -1119,7 +555,7 @@ var styles15 = StyleSheet15.create({
|
|
|
1119
555
|
fontWeight: "700",
|
|
1120
556
|
letterSpacing: 0.5
|
|
1121
557
|
},
|
|
1122
|
-
// borderRadius/height/width come from the `size` prop instead (see render-site override)
|
|
558
|
+
// borderRadius/height/width come from the `size` prop instead (see render-site override) - no
|
|
1123
559
|
// fixed default here, since this style object is shared by every caller regardless of size.
|
|
1124
560
|
trigger: {
|
|
1125
561
|
alignItems: "center",
|
|
@@ -1127,7 +563,7 @@ var styles15 = StyleSheet15.create({
|
|
|
1127
563
|
justifyContent: "center"
|
|
1128
564
|
},
|
|
1129
565
|
// Wraps just the icon/tag, inside the TouchableRipple, purely so useAutoAlign has a plain View
|
|
1130
|
-
// to attach its measurement ref to
|
|
566
|
+
// to attach its measurement ref to - TouchableRipple itself doesn't forward a ref to a
|
|
1131
567
|
// measurable native node. Padded horizontally so a multi-character tag doesn't butt right up
|
|
1132
568
|
// against the trigger's own circular edge (an icon alone doesn't need it, but this style is
|
|
1133
569
|
// shared by both).
|
|
@@ -1142,10 +578,10 @@ var styles15 = StyleSheet15.create({
|
|
|
1142
578
|
|
|
1143
579
|
// src/LabeledDropdown.tsx
|
|
1144
580
|
import { getContrastColor as getContrastColor3 } from "@rific/auto-paper";
|
|
1145
|
-
import { TouchableRipple as
|
|
1146
|
-
import { ScrollView as
|
|
1147
|
-
import { Icon as
|
|
1148
|
-
import { jsx as
|
|
581
|
+
import { TouchableRipple as TouchableRipple3 } from "@rific/feedback-press";
|
|
582
|
+
import { ScrollView as ScrollView3, StyleSheet as StyleSheet6, View as View7 } from "react-native";
|
|
583
|
+
import { Icon as Icon5, Text as Text4 } from "react-native-paper";
|
|
584
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1149
585
|
var TRIGGER_HEIGHT = 28;
|
|
1150
586
|
var POPOVER_WIDTH = 180;
|
|
1151
587
|
var ROW_HEIGHT = 36;
|
|
@@ -1168,27 +604,27 @@ function LabeledDropdown({ id, host, options, value, onChange, color, dark, alig
|
|
|
1168
604
|
onChange(option.value);
|
|
1169
605
|
host.close();
|
|
1170
606
|
};
|
|
1171
|
-
return /* @__PURE__ */
|
|
1172
|
-
/* @__PURE__ */
|
|
1173
|
-
/* @__PURE__ */
|
|
1174
|
-
/* @__PURE__ */
|
|
607
|
+
return /* @__PURE__ */ jsxs6(View7, { style: [styles6.anchor, open && styles6.anchorOpen], children: [
|
|
608
|
+
/* @__PURE__ */ jsx8(TouchableRipple3, { onPress: () => host.toggle(id), style: styles6.trigger, children: /* @__PURE__ */ jsxs6(View7, { ref: triggerRef, collapsable: false, style: styles6.triggerInner, children: [
|
|
609
|
+
/* @__PURE__ */ jsx8(Text4, { style: [styles6.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
|
|
610
|
+
/* @__PURE__ */ jsx8(Icon5, { source: "menu-down", size: 16, color })
|
|
1175
611
|
] }) }),
|
|
1176
|
-
/* @__PURE__ */
|
|
612
|
+
/* @__PURE__ */ jsx8(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ jsx8(ScrollView3, { style: [styles6.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles6.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
|
|
1177
613
|
const isSelected = option.value === value;
|
|
1178
614
|
const onColor = getContrastColor3(color);
|
|
1179
|
-
return /* @__PURE__ */
|
|
1180
|
-
option.icon && /* @__PURE__ */
|
|
1181
|
-
/* @__PURE__ */
|
|
615
|
+
return /* @__PURE__ */ jsx8(TouchableRipple3, { onPress: () => handleSelect(option), style: [styles6.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ jsxs6(View7, { style: styles6.rowInner, children: [
|
|
616
|
+
option.icon && /* @__PURE__ */ jsx8(Icon5, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
|
|
617
|
+
/* @__PURE__ */ jsx8(Text4, { style: [styles6.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
|
|
1182
618
|
] }) }, option.value);
|
|
1183
619
|
}) }) })
|
|
1184
620
|
] });
|
|
1185
621
|
}
|
|
1186
|
-
var
|
|
622
|
+
var styles6 = StyleSheet6.create({
|
|
1187
623
|
anchor: {
|
|
1188
624
|
position: "relative"
|
|
1189
625
|
},
|
|
1190
626
|
// React Native Web gives every position:'relative' view its own stacking context, so the
|
|
1191
|
-
// elevation has to live on the anchor itself
|
|
627
|
+
// elevation has to live on the anchor itself - see PopoverBody's own stacking-context notes.
|
|
1192
628
|
anchorOpen: {
|
|
1193
629
|
zIndex: 100
|
|
1194
630
|
},
|
|
@@ -1242,21 +678,21 @@ function loadSkiaWeb() {
|
|
|
1242
678
|
|
|
1243
679
|
// src/PlayerSetupPanel.tsx
|
|
1244
680
|
import { ProfilePicker } from "@tastic/profile";
|
|
1245
|
-
import { StyleSheet as
|
|
1246
|
-
import { Text as
|
|
681
|
+
import { StyleSheet as StyleSheet8, View as View8 } from "react-native";
|
|
682
|
+
import { Text as Text6 } from "react-native-paper";
|
|
1247
683
|
|
|
1248
684
|
// src/ReadyButton.tsx
|
|
1249
|
-
import { TouchableRipple as
|
|
1250
|
-
import { StyleSheet as
|
|
1251
|
-
import { Text as
|
|
1252
|
-
import { jsx as
|
|
685
|
+
import { TouchableRipple as TouchableRipple4 } from "@rific/feedback-press";
|
|
686
|
+
import { StyleSheet as StyleSheet7 } from "react-native";
|
|
687
|
+
import { Text as Text5 } from "react-native-paper";
|
|
688
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1253
689
|
function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
|
|
1254
|
-
const readyButtonStyle = [
|
|
690
|
+
const readyButtonStyle = [styles7.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
|
|
1255
691
|
const labelTextStyle = { fontFamily: labelFontFamily, fontWeight: "bold" };
|
|
1256
692
|
const labelColorStyle = { color: ready ? "#000000" : color };
|
|
1257
|
-
return /* @__PURE__ */
|
|
693
|
+
return /* @__PURE__ */ jsx9(TouchableRipple4, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ jsx9(Text5, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
|
|
1258
694
|
}
|
|
1259
|
-
var
|
|
695
|
+
var styles7 = StyleSheet7.create({
|
|
1260
696
|
readyButton: {
|
|
1261
697
|
alignItems: "center",
|
|
1262
698
|
borderRadius: 12,
|
|
@@ -1269,18 +705,18 @@ var styles17 = StyleSheet17.create({
|
|
|
1269
705
|
});
|
|
1270
706
|
|
|
1271
707
|
// src/usePopoverHost.ts
|
|
1272
|
-
import { useCallback as
|
|
708
|
+
import { useCallback as useCallback2, useState as useState2 } from "react";
|
|
1273
709
|
function usePopoverHost() {
|
|
1274
|
-
const [openId, setOpenId] =
|
|
1275
|
-
const toggle =
|
|
710
|
+
const [openId, setOpenId] = useState2(null);
|
|
711
|
+
const toggle = useCallback2((id) => {
|
|
1276
712
|
setOpenId((prev) => prev === id ? null : id);
|
|
1277
713
|
}, []);
|
|
1278
|
-
const close =
|
|
714
|
+
const close = useCallback2(() => setOpenId(null), []);
|
|
1279
715
|
return { openId, toggle, close };
|
|
1280
716
|
}
|
|
1281
717
|
|
|
1282
718
|
// src/PlayerSetupPanel.tsx
|
|
1283
|
-
import { jsx as
|
|
719
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1284
720
|
function getColorPopoverId(idPrefix) {
|
|
1285
721
|
return `${idPrefix}-color`;
|
|
1286
722
|
}
|
|
@@ -1295,27 +731,27 @@ function PlayerSetupPanel({ idPrefix, host, label, labelFontFamily, color, onCol
|
|
|
1295
731
|
const showProfilePicker = isHuman && profiles !== void 0 && !!onProfileSelect;
|
|
1296
732
|
const showCpuDifficulty = !isHuman && cpuDifficulty !== void 0 && cpuDifficultyOptions !== void 0 && !!onCpuDifficultyChange;
|
|
1297
733
|
const selectedProfile = profiles?.find((p) => p.id === selectedProfileId) ?? null;
|
|
1298
|
-
return /* @__PURE__ */
|
|
1299
|
-
showProfilePicker ? /* @__PURE__ */
|
|
1300
|
-
/* @__PURE__ */
|
|
1301
|
-
/* @__PURE__ */
|
|
734
|
+
return /* @__PURE__ */ jsxs7(View8, { style: [styles8.panel, ownPopoverOpen && styles8.panelOpen], children: [
|
|
735
|
+
showProfilePicker ? /* @__PURE__ */ jsx10(ProfilePicker, { idPrefix, host: popover, profiles, selectedId: selectedProfileId ?? null, takenId: takenProfileId, color, dark, guestLabel: guestLabel ?? "GUEST", onSelect: (profile) => onProfileSelect?.(profile), onManage: onManageProfiles }) : showCpuDifficulty ? /* @__PURE__ */ jsx10(LabeledDropdown, { id: getCpuDifficultyPopoverId(idPrefix), host: popover, options: cpuDifficultyOptions, value: cpuDifficulty, onChange: onCpuDifficultyChange, color, dark, alignOverride: cpuDifficultyAlignOverride }) : label && /* @__PURE__ */ jsx10(Text6, { variant: "labelSmall", style: { color, fontFamily: labelFontFamily }, children: label }),
|
|
736
|
+
/* @__PURE__ */ jsxs7(View8, { style: [styles8.pickerRow, pickerRowPopoverOpen && styles8.pickerRowOpen], children: [
|
|
737
|
+
/* @__PURE__ */ jsx10(InlineColorPicker, { id: getColorPopoverId(idPrefix), host: popover, value: color, onChange: onColorChange, swatches, takenValue: takenColor, allowSwapTaken, alignOverride: colorAlignOverride, rotation: colorRotation, dark, tag: isHuman ? selectedProfile?.tag : void 0, icon: isHuman ? humanIcon : cpuIcon }),
|
|
1302
738
|
secondPicker
|
|
1303
739
|
] }),
|
|
1304
|
-
isHuman && showReadyButton && onToggleReady && /* @__PURE__ */
|
|
740
|
+
isHuman && showReadyButton && onToggleReady && /* @__PURE__ */ jsx10(ReadyButton, { color, ready: ready ?? false, onToggleReady })
|
|
1305
741
|
] });
|
|
1306
742
|
}
|
|
1307
|
-
var
|
|
743
|
+
var styles8 = StyleSheet8.create({
|
|
1308
744
|
panel: {
|
|
1309
745
|
alignItems: "center",
|
|
1310
746
|
gap: 12
|
|
1311
747
|
},
|
|
1312
|
-
// See SectionedDropdown's anchorOpen comment
|
|
748
|
+
// See SectionedDropdown's anchorOpen comment - this panel is typically a sibling of another
|
|
1313
749
|
// panel (and/or a shared-controls row), so a popover escaping this panel's bounds needs the panel
|
|
1314
750
|
// itself elevated, not just the popover content, to paint above a later sibling.
|
|
1315
751
|
panelOpen: {
|
|
1316
752
|
zIndex: 100
|
|
1317
753
|
},
|
|
1318
|
-
// Same gap as `panel`'s own vertical rhythm, reused horizontally
|
|
754
|
+
// Same gap as `panel`'s own vertical rhythm, reused horizontally - the color picker and (when
|
|
1319
755
|
// present) secondPicker sit side by side within this row instead of stacked in the panel's own
|
|
1320
756
|
// column.
|
|
1321
757
|
pickerRow: {
|
|
@@ -1323,10 +759,10 @@ var styles18 = StyleSheet18.create({
|
|
|
1323
759
|
flexDirection: "row",
|
|
1324
760
|
gap: 12
|
|
1325
761
|
},
|
|
1326
|
-
// A popover is an absolutely-positioned sibling of its own trigger
|
|
762
|
+
// A popover is an absolutely-positioned sibling of its own trigger - so a tall one (the color
|
|
1327
763
|
// swatch grid) can extend down far enough to overlap the ReadyButton below, which paints on top of
|
|
1328
764
|
// it by default DOM order since it's a later sibling within `panel`. `panel`'s own panelOpen
|
|
1329
|
-
// elevation only helps this panel paint above OTHER panels (see its own comment)
|
|
765
|
+
// elevation only helps this panel paint above OTHER panels (see its own comment) - it does nothing
|
|
1330
766
|
// for stacking *within* this panel, since React Native Web gives each View its own stacking
|
|
1331
767
|
// context. This is what actually fixes that: elevate pickerRow itself above its own later sibling
|
|
1332
768
|
// whenever one of its own popovers is open.
|
|
@@ -1336,29 +772,29 @@ var styles18 = StyleSheet18.create({
|
|
|
1336
772
|
});
|
|
1337
773
|
|
|
1338
774
|
// src/PressAwayOverlay.tsx
|
|
1339
|
-
import { Pressable, StyleSheet as
|
|
1340
|
-
import { jsx as
|
|
775
|
+
import { Pressable, StyleSheet as StyleSheet9 } from "react-native";
|
|
776
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
1341
777
|
function PressAwayOverlay({ active, onPress, style }) {
|
|
1342
778
|
if (!active) return null;
|
|
1343
|
-
return /* @__PURE__ */
|
|
779
|
+
return /* @__PURE__ */ jsx11(Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [StyleSheet9.absoluteFill, style] });
|
|
1344
780
|
}
|
|
1345
781
|
|
|
1346
782
|
// src/SharedActionBand.tsx
|
|
1347
|
-
import { IconButton as
|
|
1348
|
-
import { StyleSheet as
|
|
1349
|
-
import { jsx as
|
|
783
|
+
import { IconButton as IconButton2 } from "@rific/feedback-press";
|
|
784
|
+
import { StyleSheet as StyleSheet10, View as View9 } from "react-native";
|
|
785
|
+
import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1350
786
|
function SharedActionBand({ onBack, onSettings, onRandomize, onReset, fg, popoverOpen, children }) {
|
|
1351
|
-
return /* @__PURE__ */
|
|
1352
|
-
/* @__PURE__ */
|
|
1353
|
-
/* @__PURE__ */
|
|
1354
|
-
onRandomize && /* @__PURE__ */
|
|
1355
|
-
onReset && /* @__PURE__ */
|
|
1356
|
-
/* @__PURE__ */
|
|
787
|
+
return /* @__PURE__ */ jsxs8(View9, { style: [styles9.column, popoverOpen && styles9.columnOpen], children: [
|
|
788
|
+
/* @__PURE__ */ jsxs8(View9, { style: styles9.actionsRow, children: [
|
|
789
|
+
/* @__PURE__ */ jsx12(IconButton2, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
|
|
790
|
+
onRandomize && /* @__PURE__ */ jsx12(IconButton2, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
|
|
791
|
+
onReset && /* @__PURE__ */ jsx12(IconButton2, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
|
|
792
|
+
/* @__PURE__ */ jsx12(IconButton2, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
|
|
1357
793
|
] }),
|
|
1358
794
|
children
|
|
1359
795
|
] });
|
|
1360
796
|
}
|
|
1361
|
-
var
|
|
797
|
+
var styles9 = StyleSheet10.create({
|
|
1362
798
|
actionsRow: {
|
|
1363
799
|
alignItems: "center",
|
|
1364
800
|
flexDirection: "row",
|
|
@@ -1373,86 +809,45 @@ var styles19 = StyleSheet20.create({
|
|
|
1373
809
|
}
|
|
1374
810
|
});
|
|
1375
811
|
|
|
1376
|
-
// src/StaggeredWord.tsx
|
|
1377
|
-
import { useEffect as useEffect2 } from "react";
|
|
1378
|
-
import { StyleSheet as StyleSheet21, View as View18 } from "react-native";
|
|
1379
|
-
import Animated, { Easing, useAnimatedStyle, useSharedValue, withDelay, withTiming } from "react-native-reanimated";
|
|
1380
|
-
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
1381
|
-
var STAGGER_MS = 45;
|
|
1382
|
-
var LETTER_DURATION_MS = 320;
|
|
1383
|
-
function getStaggeredWordDuration(letterCount) {
|
|
1384
|
-
return (letterCount - 1) * STAGGER_MS + LETTER_DURATION_MS;
|
|
1385
|
-
}
|
|
1386
|
-
function AnimatedLetter({ char, index, color, reducedMotion, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }) {
|
|
1387
|
-
const progress = useSharedValue(reducedMotion ? 1 : 0);
|
|
1388
|
-
useEffect2(() => {
|
|
1389
|
-
if (reducedMotion) return;
|
|
1390
|
-
progress.value = withDelay(index * STAGGER_MS, withTiming(1, { duration: LETTER_DURATION_MS, easing: Easing.out(Easing.back(1.5)) }));
|
|
1391
|
-
}, [reducedMotion]);
|
|
1392
|
-
const style = useAnimatedStyle(() => ({
|
|
1393
|
-
opacity: progress.value,
|
|
1394
|
-
transform: [{ translateY: (1 - progress.value) * 12 }, { scale: 0.85 + progress.value * 0.15 }]
|
|
1395
|
-
}));
|
|
1396
|
-
return /* @__PURE__ */ jsx24(Animated.Text, { allowFontScaling: false, style: [{ color, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, style], children: char });
|
|
1397
|
-
}
|
|
1398
|
-
function StaggeredWord({ word, color, fontFamily, fontSize, lineHeight, reducedMotion, startIndex = 0, fontWeight = "bold", letterSpacing, style, onLayout }) {
|
|
1399
|
-
return /* @__PURE__ */ jsx24(View18, { style: [styles20.row, style], onLayout, children: word.split("").map((char, i) => /* @__PURE__ */ jsx24(AnimatedLetter, { char, index: startIndex + i, color, reducedMotion, fontFamily, fontSize, lineHeight, fontWeight, letterSpacing }, i)) });
|
|
1400
|
-
}
|
|
1401
|
-
var styles20 = StyleSheet21.create({
|
|
1402
|
-
row: {
|
|
1403
|
-
flexDirection: "row"
|
|
1404
|
-
}
|
|
1405
|
-
});
|
|
1406
|
-
|
|
1407
|
-
// src/UpdateDialog.tsx
|
|
1408
|
-
import { useUpdater as useUpdater2 } from "@rific/updater";
|
|
1409
|
-
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
1410
|
-
function UpdateDialog({ rotation = 0, autoCheck = true, autoPrompt = true, onError }) {
|
|
1411
|
-
const { manifest, onConfirm, respond } = useUpdateConfirmBridge();
|
|
1412
|
-
useUpdater2({ autoCheck, autoPrompt, onConfirm, onError });
|
|
1413
|
-
const date = manifest ? new Date(manifest.createdAt) : null;
|
|
1414
|
-
return /* @__PURE__ */ jsx25(ConfirmDialog, { visible: !!manifest, title: "Update Available", message: date ? `Released ${date.toLocaleDateString()} at ${date.toLocaleTimeString()}` : "", confirmLabel: "Restart", cancelLabel: "Later", icon: "update", rotation, onConfirm: () => respond(true), onCancel: () => respond(false) });
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
812
|
// src/useQuitConfirmation.ts
|
|
1418
|
-
import { useCallback as
|
|
813
|
+
import { useCallback as useCallback3, useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
|
|
1419
814
|
function useQuitConfirmation(hasProgress, onConfirmedBack) {
|
|
1420
|
-
const [confirmVisible, setConfirmVisible] =
|
|
1421
|
-
const hasProgressRef =
|
|
1422
|
-
|
|
815
|
+
const [confirmVisible, setConfirmVisible] = useState3(false);
|
|
816
|
+
const hasProgressRef = useRef2(hasProgress);
|
|
817
|
+
useEffect2(() => {
|
|
1423
818
|
hasProgressRef.current = hasProgress;
|
|
1424
819
|
});
|
|
1425
|
-
const onConfirmedBackRef =
|
|
1426
|
-
|
|
820
|
+
const onConfirmedBackRef = useRef2(onConfirmedBack);
|
|
821
|
+
useEffect2(() => {
|
|
1427
822
|
onConfirmedBackRef.current = onConfirmedBack;
|
|
1428
823
|
});
|
|
1429
|
-
const requestBack =
|
|
824
|
+
const requestBack = useCallback3(() => {
|
|
1430
825
|
if (hasProgressRef.current()) setConfirmVisible(true);
|
|
1431
826
|
else onConfirmedBackRef.current();
|
|
1432
827
|
}, []);
|
|
1433
|
-
const cancelBack =
|
|
828
|
+
const cancelBack = useCallback3(() => setConfirmVisible(false), []);
|
|
1434
829
|
return { confirmVisible, requestBack, cancelBack };
|
|
1435
830
|
}
|
|
1436
831
|
|
|
1437
832
|
// src/useZoneClampedAlign.ts
|
|
1438
833
|
import { useZoneBounds } from "@tastic/split-screen";
|
|
1439
|
-
import { useEffect as
|
|
1440
|
-
import { useWindowDimensions as
|
|
1441
|
-
import { useSafeAreaInsets
|
|
834
|
+
import { useEffect as useEffect3, useState as useState4 } from "react";
|
|
835
|
+
import { useWindowDimensions as useWindowDimensions3 } from "react-native";
|
|
836
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
1442
837
|
var EDGE_MARGIN2 = 12;
|
|
1443
838
|
function useZoneClampedAlign(open, contentWidth, contentHeight, rotation = 0) {
|
|
1444
839
|
const auto = useAutoAlign(open, contentWidth, contentHeight, rotation);
|
|
1445
840
|
const zone = useZoneBounds();
|
|
1446
|
-
const { height: windowHeight } =
|
|
1447
|
-
const insets =
|
|
841
|
+
const { height: windowHeight } = useWindowDimensions3();
|
|
842
|
+
const insets = useSafeAreaInsets();
|
|
1448
843
|
const shouldMeasure = open && auto.measured && zone !== null;
|
|
1449
|
-
const [ownBounds, setOwnBounds] =
|
|
1450
|
-
const [prevShouldMeasure, setPrevShouldMeasure] =
|
|
844
|
+
const [ownBounds, setOwnBounds] = useState4(null);
|
|
845
|
+
const [prevShouldMeasure, setPrevShouldMeasure] = useState4(shouldMeasure);
|
|
1451
846
|
if (shouldMeasure !== prevShouldMeasure) {
|
|
1452
847
|
setPrevShouldMeasure(shouldMeasure);
|
|
1453
848
|
setOwnBounds(null);
|
|
1454
849
|
}
|
|
1455
|
-
|
|
850
|
+
useEffect3(() => {
|
|
1456
851
|
if (!shouldMeasure) return;
|
|
1457
852
|
auto.triggerRef.current?.measureInWindow((_x, y, _width, height) => {
|
|
1458
853
|
const belowShared = zone.zoneSide === "belowShared";
|