@tastic/hud 0.7.0 → 0.8.2
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 +48 -8
- package/dist/SkiaGate.d.mts +13 -0
- package/dist/SkiaGate.d.ts +13 -0
- package/dist/SkiaGate.js +45 -0
- package/dist/SkiaGate.mjs +6 -0
- package/dist/chunk-XEOFR236.mjs +21 -0
- package/dist/index.d.mts +20 -17
- package/dist/index.d.ts +20 -17
- package/dist/index.js +250 -214
- package/dist/index.mjs +166 -145
- package/package.json +13 -6
- package/src/BaseSettingsDialog.tsx +9 -6
- package/src/BaseStatsScreen.tsx +54 -93
- package/src/ConfirmDialog.tsx +29 -3
- package/src/InlineColorPicker.tsx +10 -4
- package/src/SectionedDropdown.tsx +17 -3
- package/src/SkiaGate.tsx +45 -0
- package/src/SkiaGate.web.tsx +9 -0
- package/src/TriggerGaugeHost.tsx +12 -20
- package/src/index.ts +8 -7
- package/src/useAutoAlign.ts +58 -16
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SkiaGate
|
|
3
|
+
} from "./chunk-XEOFR236.mjs";
|
|
4
|
+
|
|
1
5
|
// src/AchievementRow.tsx
|
|
2
6
|
import { useAutoPaperTheme } from "@rific/auto-paper";
|
|
3
7
|
import { StyleSheet, View } from "react-native";
|
|
@@ -70,7 +74,7 @@ var styles = StyleSheet.create({
|
|
|
70
74
|
import { AutoAppearancePicker, Dialog, useAutoPaperTheme as useAutoPaperTheme2 } from "@rific/auto-paper";
|
|
71
75
|
import { Button, SoundContext, TouchableRipple, useHapticSettings, useSoundSettings, useVibration } from "@rific/feedback-press";
|
|
72
76
|
import { useUpdater } from "@rific/updater";
|
|
73
|
-
import { useIsTouchPrimaryDevice } from "@tastic/core";
|
|
77
|
+
import { useIsTouchPrimaryDevice, useRotation } from "@tastic/core";
|
|
74
78
|
import { useContext, useState } from "react";
|
|
75
79
|
import { Platform, ScrollView, StyleSheet as StyleSheet2, View as View2 } from "react-native";
|
|
76
80
|
import { Icon as Icon2, Portal, Text as Text2 } from "react-native-paper";
|
|
@@ -78,8 +82,10 @@ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
|
78
82
|
function SettingIcon({ source, color, containerColor }) {
|
|
79
83
|
return /* @__PURE__ */ jsx2(View2, { style: [styles2.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ jsx2(Icon2, { source, size: 18, color }) });
|
|
80
84
|
}
|
|
81
|
-
function BaseSettingsDialog({ visible, onDismiss, rotation
|
|
85
|
+
function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, children }) {
|
|
82
86
|
const { dark, colors } = useAutoPaperTheme2();
|
|
87
|
+
const ambientRotation = useRotation();
|
|
88
|
+
const rotation = rotationOverride ?? ambientRotation;
|
|
83
89
|
const isTouchPrimary = useIsTouchPrimaryDevice();
|
|
84
90
|
const { settings: hapticSettings, set: setHapticSettings } = useHapticSettings();
|
|
85
91
|
const { settings: soundSettings, set: setSoundSettings } = useSoundSettings();
|
|
@@ -269,136 +275,56 @@ var styles2 = StyleSheet2.create({
|
|
|
269
275
|
});
|
|
270
276
|
|
|
271
277
|
// src/BaseStatsScreen.tsx
|
|
272
|
-
import { useAutoPaperTheme as
|
|
273
|
-
import { Button as
|
|
278
|
+
import { useAutoPaperTheme as useAutoPaperTheme4 } from "@rific/auto-paper";
|
|
279
|
+
import { Button as Button3, IconButton } from "@rific/feedback-press";
|
|
280
|
+
import { useRotation as useRotation2 } from "@tastic/core";
|
|
274
281
|
import { useState as useState2 } from "react";
|
|
275
|
-
import { ScrollView as ScrollView2, StyleSheet as
|
|
276
|
-
import {
|
|
277
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
278
|
-
function BaseStatsScreen({ rotation = 0, 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, cardBg: cardBgOverride, accentColor: accentColorOverride, children }) {
|
|
279
|
-
const { dark, colors } = useAutoPaperTheme3();
|
|
280
|
-
const [confirmResetVisible, setConfirmResetVisible] = useState2(false);
|
|
281
|
-
const showReset = !!onReset;
|
|
282
|
-
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
283
|
-
const bg = bgOverride ?? (dark ? "#000000" : "#FFFFFF");
|
|
284
|
-
const cardBg = cardBgOverride ?? (dark ? "#111111" : "#F2F2F2");
|
|
285
|
-
const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
|
|
286
|
-
const accentColor = accentColorOverride ?? colors.secondary;
|
|
287
|
-
return /* @__PURE__ */ jsxs3(View3, { style: [styles3.container, { backgroundColor: bg }], children: [
|
|
288
|
-
/* @__PURE__ */ jsxs3(View3, { style: [styles3.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
|
|
289
|
-
/* @__PURE__ */ jsx3(IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
|
|
290
|
-
/* @__PURE__ */ jsx3(Text3, { variant: titleVariant, style: [styles3.title, { color: fg }], children: title })
|
|
291
|
-
] }),
|
|
292
|
-
/* @__PURE__ */ jsxs3(ScrollView2, { style: styles3.scrollView, contentContainerStyle: [styles3.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
|
|
293
|
-
children,
|
|
294
|
-
showReset && /* @__PURE__ */ jsx3(Button2, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.secondary, textColor: colors.onSecondary, style: styles3.resetButton, children: resetLabel })
|
|
295
|
-
] }),
|
|
296
|
-
confirmResetVisible && /* @__PURE__ */ jsx3(Portal2, { children: /* @__PURE__ */ jsx3(View3, { style: [styles3.overlay, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: /* @__PURE__ */ jsxs3(View3, { style: [styles3.overlayCard, { backgroundColor: cardBg, borderColor: cardBorder }], children: [
|
|
297
|
-
/* @__PURE__ */ jsx3(Icon3, { source: "alert-outline", size: 64, color: accentColor }),
|
|
298
|
-
/* @__PURE__ */ jsx3(Text3, { variant: "headlineLarge", style: [styles3.overlayTitle, { color: accentColor }], children: resetConfirmTitle }),
|
|
299
|
-
/* @__PURE__ */ jsx3(Text3, { variant: "bodyLarge", style: [styles3.overlayBody, { color: fg }], children: resetConfirmBody }),
|
|
300
|
-
/* @__PURE__ */ jsx3(Button2, { mode: "contained", onPress: () => setConfirmResetVisible(false), style: styles3.overlayButton, children: "Cancel" }),
|
|
301
|
-
/* @__PURE__ */ jsx3(
|
|
302
|
-
Button2,
|
|
303
|
-
{
|
|
304
|
-
mode: "contained",
|
|
305
|
-
onPress: () => {
|
|
306
|
-
onReset?.();
|
|
307
|
-
setConfirmResetVisible(false);
|
|
308
|
-
},
|
|
309
|
-
style: styles3.overlayButton,
|
|
310
|
-
buttonColor: colors.secondary,
|
|
311
|
-
textColor: colors.onSecondary,
|
|
312
|
-
children: "Reset"
|
|
313
|
-
}
|
|
314
|
-
)
|
|
315
|
-
] }) }) })
|
|
316
|
-
] });
|
|
317
|
-
}
|
|
318
|
-
var styles3 = StyleSheet3.create({
|
|
319
|
-
container: {
|
|
320
|
-
flex: 1
|
|
321
|
-
},
|
|
322
|
-
content: {
|
|
323
|
-
gap: 12,
|
|
324
|
-
paddingHorizontal: 20
|
|
325
|
-
},
|
|
326
|
-
header: {
|
|
327
|
-
alignItems: "center",
|
|
328
|
-
flexDirection: "row",
|
|
329
|
-
gap: 4,
|
|
330
|
-
paddingBottom: 8
|
|
331
|
-
},
|
|
332
|
-
// Same overlay shape BaseSettingsDialog's own info overlay uses (backdrop + centered card),
|
|
333
|
-
// rotated in place the same way rather than via Portal-root transform for the same reason — see
|
|
334
|
-
// that component's own identical styles for the full rationale.
|
|
335
|
-
overlay: {
|
|
336
|
-
alignItems: "center",
|
|
337
|
-
backgroundColor: "rgba(0,0,0,0.72)",
|
|
338
|
-
bottom: 0,
|
|
339
|
-
justifyContent: "center",
|
|
340
|
-
left: 0,
|
|
341
|
-
position: "absolute",
|
|
342
|
-
right: 0,
|
|
343
|
-
top: 0
|
|
344
|
-
},
|
|
345
|
-
overlayBody: { textAlign: "center" },
|
|
346
|
-
overlayButton: { width: 160 },
|
|
347
|
-
overlayCard: {
|
|
348
|
-
alignItems: "center",
|
|
349
|
-
borderRadius: 20,
|
|
350
|
-
borderWidth: 1,
|
|
351
|
-
gap: 16,
|
|
352
|
-
maxWidth: 360,
|
|
353
|
-
padding: 32
|
|
354
|
-
},
|
|
355
|
-
overlayTitle: { fontWeight: "bold", marginBottom: -8 },
|
|
356
|
-
resetButton: {
|
|
357
|
-
marginTop: 16
|
|
358
|
-
},
|
|
359
|
-
// Bounds the ScrollView to the space `container`'s flex:1 actually gives it — without this, a
|
|
360
|
-
// ScrollView with only a contentContainerStyle isn't reliably height-constrained on native (it
|
|
361
|
-
// can render at its full unclipped content height instead of the screen's), which leaves the
|
|
362
|
-
// header's back button touch target unreliable underneath it.
|
|
363
|
-
scrollView: {
|
|
364
|
-
flex: 1
|
|
365
|
-
},
|
|
366
|
-
title: {
|
|
367
|
-
flexShrink: 1,
|
|
368
|
-
fontWeight: "bold"
|
|
369
|
-
}
|
|
370
|
-
});
|
|
282
|
+
import { ScrollView as ScrollView2, StyleSheet as StyleSheet4, View as View4 } from "react-native";
|
|
283
|
+
import { Text as Text4 } from "react-native-paper";
|
|
371
284
|
|
|
372
285
|
// src/ConfirmDialog.tsx
|
|
373
|
-
import { useAutoPaperTheme as
|
|
374
|
-
import { Button as
|
|
375
|
-
import { StyleSheet as
|
|
376
|
-
import { Icon as
|
|
377
|
-
import { jsx as
|
|
286
|
+
import { useAutoPaperTheme as useAutoPaperTheme3 } from "@rific/auto-paper";
|
|
287
|
+
import { Button as Button2 } from "@rific/feedback-press";
|
|
288
|
+
import { StyleSheet as StyleSheet3, View as View3 } from "react-native";
|
|
289
|
+
import { Icon as Icon3, Portal as Portal2, Text as Text3 } from "react-native-paper";
|
|
290
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
378
291
|
function ConfirmDialog({ visible, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, rotation = 0, icon = "help-circle-outline", destructive = false }) {
|
|
379
|
-
const { dark, colors } =
|
|
292
|
+
const { dark, colors } = useAutoPaperTheme3();
|
|
380
293
|
if (!visible) return null;
|
|
381
294
|
const fg = dark ? "#FFFFFF" : "#000000";
|
|
382
295
|
const cardBg = dark ? "#111111" : "#F2F2F2";
|
|
383
296
|
const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
|
|
384
297
|
const accent = destructive ? colors.danger : colors.primary;
|
|
385
298
|
const onAccent = destructive ? colors.onDanger : colors.onPrimary;
|
|
386
|
-
return /* @__PURE__ */
|
|
387
|
-
/* @__PURE__ */
|
|
388
|
-
/* @__PURE__ */
|
|
389
|
-
/* @__PURE__ */
|
|
390
|
-
/* @__PURE__ */
|
|
391
|
-
/* @__PURE__ */
|
|
392
|
-
/* @__PURE__ */
|
|
299
|
+
return /* @__PURE__ */ jsx3(Portal2, { children: /* @__PURE__ */ jsx3(View3, { style: styles3.overlay, children: /* @__PURE__ */ jsxs3(View3, { style: [styles3.card, { backgroundColor: cardBg, borderColor: cardBorder }, rotation % 360 !== 0 && { transform: [{ rotate: `${rotation}deg` }] }], children: [
|
|
300
|
+
/* @__PURE__ */ jsx3(Icon3, { source: icon, size: 64, color: accent }),
|
|
301
|
+
/* @__PURE__ */ jsx3(Text3, { variant: "headlineLarge", style: [styles3.title, { color: accent }], children: title }),
|
|
302
|
+
/* @__PURE__ */ jsx3(Text3, { variant: "bodyLarge", style: [styles3.body, { color: fg }], children: message }),
|
|
303
|
+
/* @__PURE__ */ jsxs3(View3, { style: styles3.actions, children: [
|
|
304
|
+
/* @__PURE__ */ jsx3(Button2, { mode: "outlined", onPress: onCancel, style: styles3.actionButton, children: cancelLabel }),
|
|
305
|
+
/* @__PURE__ */ jsx3(Button2, { mode: "contained", onPress: onConfirm, style: styles3.actionButton, buttonColor: accent, textColor: onAccent, children: confirmLabel })
|
|
393
306
|
] })
|
|
394
307
|
] }) }) });
|
|
395
308
|
}
|
|
396
|
-
var
|
|
309
|
+
var styles3 = StyleSheet3.create({
|
|
310
|
+
// flexBasis (not flex: 1's own 0%) is each button's real floor: whenever both fit side by side,
|
|
311
|
+
// flexGrow still splits any leftover width evenly beyond that floor, same visual result as the
|
|
312
|
+
// old flex: 1 on a roomy screen - but a button can never be squeezed narrower than 128, only
|
|
313
|
+
// wrap to its own row instead (see actions' flexWrap below). react-native-paper's Button
|
|
314
|
+
// hardcodes numberOfLines={1} on its label with no override, so a button squeezed under its
|
|
315
|
+
// label's natural width silently ellipsizes ("Cancel" -> "Can...") instead of wrapping - assumed
|
|
316
|
+
// fixable by just giving the row more width, until confirmed live that these two-word labels
|
|
317
|
+
// (Cancel/Reset/New Game) can still lose the truncation fight even with the extra room card's own
|
|
318
|
+
// width fix below provides, on a narrow enough screen. flexBasis is the real fix: past a certain
|
|
319
|
+
// width there's no side-by-side arrangement that fits both buttons at their natural label size,
|
|
320
|
+
// so the layout has to give (stack) rather than the label (truncate).
|
|
397
321
|
actionButton: {
|
|
398
|
-
|
|
322
|
+
flexBasis: 128,
|
|
323
|
+
flexGrow: 1
|
|
399
324
|
},
|
|
400
325
|
actions: {
|
|
401
326
|
flexDirection: "row",
|
|
327
|
+
flexWrap: "wrap",
|
|
402
328
|
gap: 12,
|
|
403
329
|
width: "100%"
|
|
404
330
|
},
|
|
@@ -411,7 +337,12 @@ var styles4 = StyleSheet4.create({
|
|
|
411
337
|
borderWidth: 1,
|
|
412
338
|
gap: 16,
|
|
413
339
|
maxWidth: 360,
|
|
414
|
-
padding: 32
|
|
340
|
+
padding: 32,
|
|
341
|
+
// Claims its full allowance instead of just maxWidth alone, which only caps how wide the card
|
|
342
|
+
// is allowed to get - without an explicit width, a flex-centered child sizes to its own content
|
|
343
|
+
// instead of stretching, so the card (and therefore actions below) can end up narrower than the
|
|
344
|
+
// screen actually has room for, needlessly forcing the action row to wrap sooner than it has to.
|
|
345
|
+
width: "100%"
|
|
415
346
|
},
|
|
416
347
|
overlay: {
|
|
417
348
|
alignItems: "center",
|
|
@@ -419,11 +350,88 @@ var styles4 = StyleSheet4.create({
|
|
|
419
350
|
bottom: 0,
|
|
420
351
|
justifyContent: "center",
|
|
421
352
|
left: 0,
|
|
353
|
+
// Gives the card room to breathe from the screen edges now that it reliably stretches to fill
|
|
354
|
+
// its full width allowance (see card's own width comment) - without this, width: '100%' would
|
|
355
|
+
// run the card edge-to-edge on a screen narrower than maxWidth's own 360.
|
|
356
|
+
paddingHorizontal: 24,
|
|
422
357
|
position: "absolute",
|
|
423
358
|
right: 0,
|
|
424
359
|
top: 0
|
|
425
360
|
},
|
|
361
|
+
// textAlign is load-bearing, not decorative, once `title` wraps to more than one line - centered
|
|
362
|
+
// within card's own alignItems: 'center' only centers the Text's block as a whole; each wrapped
|
|
363
|
+
// line inside that block still defaults to left-aligned without this.
|
|
364
|
+
title: {
|
|
365
|
+
fontWeight: "bold",
|
|
366
|
+
textAlign: "center"
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// src/BaseStatsScreen.tsx
|
|
371
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
372
|
+
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 }) {
|
|
373
|
+
const { dark, colors } = useAutoPaperTheme4();
|
|
374
|
+
const ambientRotation = useRotation2();
|
|
375
|
+
const rotation = rotationOverride ?? ambientRotation;
|
|
376
|
+
const [confirmResetVisible, setConfirmResetVisible] = useState2(false);
|
|
377
|
+
const showReset = !!onReset;
|
|
378
|
+
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
379
|
+
const bg = bgOverride ?? (dark ? "#000000" : "#FFFFFF");
|
|
380
|
+
return /* @__PURE__ */ jsxs4(View4, { style: [styles4.container, { backgroundColor: bg }], children: [
|
|
381
|
+
/* @__PURE__ */ jsxs4(View4, { style: [styles4.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
|
|
382
|
+
/* @__PURE__ */ jsx4(IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: onBack }),
|
|
383
|
+
/* @__PURE__ */ jsx4(Text4, { variant: titleVariant, style: [styles4.title, { color: fg }], children: title })
|
|
384
|
+
] }),
|
|
385
|
+
/* @__PURE__ */ jsxs4(ScrollView2, { style: styles4.scrollView, contentContainerStyle: [styles4.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
|
|
386
|
+
children,
|
|
387
|
+
showReset && /* @__PURE__ */ jsx4(Button3, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.danger, textColor: colors.onDanger, style: styles4.resetButton, children: resetLabel })
|
|
388
|
+
] }),
|
|
389
|
+
/* @__PURE__ */ jsx4(
|
|
390
|
+
ConfirmDialog,
|
|
391
|
+
{
|
|
392
|
+
visible: confirmResetVisible,
|
|
393
|
+
title: resetConfirmTitle,
|
|
394
|
+
message: resetConfirmBody,
|
|
395
|
+
confirmLabel: "Reset",
|
|
396
|
+
cancelLabel: "Cancel",
|
|
397
|
+
icon: "alert-outline",
|
|
398
|
+
destructive: true,
|
|
399
|
+
rotation,
|
|
400
|
+
onCancel: () => setConfirmResetVisible(false),
|
|
401
|
+
onConfirm: () => {
|
|
402
|
+
onReset?.();
|
|
403
|
+
setConfirmResetVisible(false);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
)
|
|
407
|
+
] });
|
|
408
|
+
}
|
|
409
|
+
var styles4 = StyleSheet4.create({
|
|
410
|
+
container: {
|
|
411
|
+
flex: 1
|
|
412
|
+
},
|
|
413
|
+
content: {
|
|
414
|
+
gap: 12,
|
|
415
|
+
paddingHorizontal: 20
|
|
416
|
+
},
|
|
417
|
+
header: {
|
|
418
|
+
alignItems: "center",
|
|
419
|
+
flexDirection: "row",
|
|
420
|
+
gap: 4,
|
|
421
|
+
paddingBottom: 8
|
|
422
|
+
},
|
|
423
|
+
resetButton: {
|
|
424
|
+
marginTop: 16
|
|
425
|
+
},
|
|
426
|
+
// Bounds the ScrollView to the space `container`'s flex:1 actually gives it — without this, a
|
|
427
|
+
// ScrollView with only a contentContainerStyle isn't reliably height-constrained on native (it
|
|
428
|
+
// can render at its full unclipped content height instead of the screen's), which leaves the
|
|
429
|
+
// header's back button touch target unreliable underneath it.
|
|
430
|
+
scrollView: {
|
|
431
|
+
flex: 1
|
|
432
|
+
},
|
|
426
433
|
title: {
|
|
434
|
+
flexShrink: 1,
|
|
427
435
|
fontWeight: "bold"
|
|
428
436
|
}
|
|
429
437
|
});
|
|
@@ -459,8 +467,9 @@ import { Platform as Platform3 } from "react-native";
|
|
|
459
467
|
// src/SectionedDropdown.tsx
|
|
460
468
|
import { getBlendedColor, getColorRoles } from "@rific/auto-paper";
|
|
461
469
|
import { IconButton as IconButton2, TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
|
|
470
|
+
import { useRotation as useRotation3 } from "@tastic/core";
|
|
462
471
|
import { ScrollView as ScrollView3, StyleSheet as StyleSheet7, View as View8 } from "react-native";
|
|
463
|
-
import { Icon as
|
|
472
|
+
import { Icon as Icon4, Text as Text5 } from "react-native-paper";
|
|
464
473
|
|
|
465
474
|
// src/fonts.ts
|
|
466
475
|
import { Platform as Platform2 } from "react-native";
|
|
@@ -532,26 +541,27 @@ var styles6 = StyleSheet6.create({
|
|
|
532
541
|
}
|
|
533
542
|
});
|
|
534
543
|
|
|
535
|
-
// src/TriggerGaugeHost.tsx
|
|
536
|
-
import { lazy, Suspense } from "react";
|
|
537
|
-
|
|
538
|
-
// src/loadSkiaWeb.ts
|
|
539
|
-
function loadSkiaWeb() {
|
|
540
|
-
return Promise.resolve();
|
|
541
|
-
}
|
|
542
|
-
|
|
543
544
|
// src/TriggerGaugeHost.tsx
|
|
544
545
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
545
|
-
var
|
|
546
|
+
var getTriggerGauge = () => import("./TriggerGauge-2OJOOF6I.mjs").then((mod) => ({ default: mod.TriggerGauge }));
|
|
546
547
|
function TriggerGaugeHost(props) {
|
|
547
|
-
return /* @__PURE__ */ jsx7(
|
|
548
|
+
return /* @__PURE__ */ jsx7(SkiaGate, { getComponent: getTriggerGauge, componentProps: props });
|
|
548
549
|
}
|
|
549
550
|
|
|
550
551
|
// src/useAutoAlign.ts
|
|
551
552
|
import { useCallback, useEffect, useRef, useState as useState3 } from "react";
|
|
552
553
|
import { useWindowDimensions as useWindowDimensions2 } from "react-native";
|
|
553
554
|
var EDGE_MARGIN = 12;
|
|
554
|
-
function
|
|
555
|
+
function rotateRect(x, y, width, height, rotation, windowWidth, windowHeight) {
|
|
556
|
+
if (rotation === 0) return { x, y, width, height };
|
|
557
|
+
const cx = windowWidth / 2;
|
|
558
|
+
const cy = windowHeight / 2;
|
|
559
|
+
const dx = x + width / 2 - cx;
|
|
560
|
+
const dy = y + height / 2 - cy;
|
|
561
|
+
const [newDx, newDy, newWidth, newHeight] = rotation === 180 ? [-dx, -dy, width, height] : rotation === 90 ? [-dy, dx, height, width] : [dy, -dx, height, width];
|
|
562
|
+
return { x: cx + newDx - newWidth / 2, y: cy + newDy - newHeight / 2, width: newWidth, height: newHeight };
|
|
563
|
+
}
|
|
564
|
+
function useAutoAlign(open, contentWidth, contentHeight, rotation = 0) {
|
|
555
565
|
const triggerRef = useRef(null);
|
|
556
566
|
const { width: windowWidth, height: windowHeight } = useWindowDimensions2();
|
|
557
567
|
const [align, setAlign] = useState3("center");
|
|
@@ -564,7 +574,8 @@ function useAutoAlign(open, contentWidth, contentHeight) {
|
|
|
564
574
|
if (open) setMeasured(false);
|
|
565
575
|
}
|
|
566
576
|
const measure = useCallback(() => {
|
|
567
|
-
triggerRef.current?.measureInWindow((
|
|
577
|
+
triggerRef.current?.measureInWindow((rawX, rawY, rawWidth, rawHeight) => {
|
|
578
|
+
const { x, y, width: triggerWidth, height: triggerHeight } = rotateRect(rawX, rawY, rawWidth, rawHeight, rotation, windowWidth, windowHeight);
|
|
568
579
|
const centerX = x + triggerWidth / 2;
|
|
569
580
|
const overflowsRight = centerX + contentWidth / 2 > windowWidth - EDGE_MARGIN;
|
|
570
581
|
const overflowsLeft = centerX - contentWidth / 2 < EDGE_MARGIN;
|
|
@@ -578,7 +589,7 @@ function useAutoAlign(open, contentWidth, contentHeight) {
|
|
|
578
589
|
setMaxHeight(Math.max(above ? roomAbove : roomBelow, 0));
|
|
579
590
|
setMeasured(true);
|
|
580
591
|
});
|
|
581
|
-
}, [contentWidth, contentHeight, windowWidth, windowHeight]);
|
|
592
|
+
}, [contentWidth, contentHeight, windowWidth, windowHeight, rotation]);
|
|
582
593
|
useEffect(() => {
|
|
583
594
|
if (open) measure();
|
|
584
595
|
}, [open, measure]);
|
|
@@ -597,7 +608,9 @@ var MENU_ITEM_HEIGHT = 40;
|
|
|
597
608
|
var MENU_ITEM_HEIGHT_WITH_DESCRIPTION = 58;
|
|
598
609
|
var MENU_DIVIDER_HEIGHT = 9;
|
|
599
610
|
var MENU_VERTICAL_PADDING = 12;
|
|
600
|
-
function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accentColor, mutedColor, onAccentColor, dark, align: alignOverride, autoDismiss = true, labelFontFamily = MONO_FONT, allClearLabels = { all: "All", clear: "Clear" } }) {
|
|
611
|
+
function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accentColor, mutedColor, onAccentColor, dark, align: alignOverride, rotation: rotationOverride, autoDismiss = true, labelFontFamily = MONO_FONT, allClearLabels = { all: "All", clear: "Clear" } }) {
|
|
612
|
+
const ambientRotation = useRotation3();
|
|
613
|
+
const rotation = rotationOverride ?? ambientRotation;
|
|
601
614
|
const menuBg = dark ? "#000000" : "#FFFFFF";
|
|
602
615
|
const menuBorderColor = getBlendedColor(mutedColor, menuBg, 0.5);
|
|
603
616
|
const resolvedOnAccentColor = onAccentColor ?? getColorRoles(accentColor, menuBg).onColor;
|
|
@@ -609,7 +622,7 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
|
|
|
609
622
|
const divider = sectionIndex > 0 ? MENU_DIVIDER_HEIGHT : 0;
|
|
610
623
|
return sum + rows + footer + divider;
|
|
611
624
|
}, 0);
|
|
612
|
-
const { align: autoAlign, maxHeight, measured, triggerRef, verticalAlign } = useAutoAlign(open, MENU_MAX_WIDTH, estimatedHeight);
|
|
625
|
+
const { align: autoAlign, maxHeight, measured, triggerRef, verticalAlign } = useAutoAlign(open, MENU_MAX_WIDTH, estimatedHeight, rotation);
|
|
613
626
|
const align = alignOverride ?? autoAlign;
|
|
614
627
|
const onlySection = sections.length === 1 ? sections[0] : null;
|
|
615
628
|
const selectedOption = onlySection?.kind === "single" ? onlySection.options.find((o) => o.value === onlySection.value) : void 0;
|
|
@@ -675,7 +688,7 @@ function SingleSection({
|
|
|
675
688
|
const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
|
|
676
689
|
const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
|
|
677
690
|
return /* @__PURE__ */ jsx8(TouchableRipple2, { disabled: taken, onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ jsxs7(View8, { style: styles7.itemRow, children: [
|
|
678
|
-
option.icon && /* @__PURE__ */ jsx8(
|
|
691
|
+
option.icon && /* @__PURE__ */ jsx8(Icon4, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
|
|
679
692
|
/* @__PURE__ */ jsxs7(View8, { style: styles7.itemLabelColumn, children: [
|
|
680
693
|
/* @__PURE__ */ jsx8(Text5, { variant: "labelLarge", style: labelStyle, children: option.label }),
|
|
681
694
|
option.description && /* @__PURE__ */ jsx8(Text5, { variant: "labelSmall", style: descriptionStyle, children: option.description })
|
|
@@ -701,7 +714,7 @@ function MultiSection({
|
|
|
701
714
|
const itemStyle = [styles7.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
|
|
702
715
|
const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
|
|
703
716
|
return /* @__PURE__ */ jsx8(TouchableRipple2, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ jsxs7(View8, { style: styles7.itemRow, children: [
|
|
704
|
-
option.icon && /* @__PURE__ */ jsx8(
|
|
717
|
+
option.icon && /* @__PURE__ */ jsx8(Icon4, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
|
|
705
718
|
/* @__PURE__ */ jsx8(Text5, { variant: "labelLarge", style: [styles7.itemLabelFlex, labelStyle], children: option.label })
|
|
706
719
|
] }) }, option.value);
|
|
707
720
|
}),
|
|
@@ -806,11 +819,11 @@ var styles8 = StyleSheet8.create({
|
|
|
806
819
|
|
|
807
820
|
// src/CornerStatusBadges.tsx
|
|
808
821
|
import { StyleSheet as StyleSheet9, View as View9 } from "react-native";
|
|
809
|
-
import { Icon as
|
|
822
|
+
import { Icon as Icon5 } from "react-native-paper";
|
|
810
823
|
import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
811
824
|
var BADGE_SIZE = 30;
|
|
812
825
|
function StatusBadge({ icon }) {
|
|
813
|
-
return /* @__PURE__ */ jsx11(View9, { style: styles9.badge, children: icon && /* @__PURE__ */ jsx11(
|
|
826
|
+
return /* @__PURE__ */ jsx11(View9, { style: styles9.badge, children: icon && /* @__PURE__ */ jsx11(Icon5, { source: icon, size: 16, color: "rgba(255,255,255,0.85)" }) });
|
|
814
827
|
}
|
|
815
828
|
function CornerStatusBadges({ orientationMode, p1OnRight, p1, p2 }) {
|
|
816
829
|
const p1OnRightSide = orientationMode === "sideBySide" && p1OnRight;
|
|
@@ -841,9 +854,9 @@ var styles9 = StyleSheet9.create({
|
|
|
841
854
|
// src/InlineColorPicker.tsx
|
|
842
855
|
import { defaultColors, getContrastColor } from "@rific/auto-paper";
|
|
843
856
|
import { TouchableRipple as TouchableRipple3 } from "@rific/feedback-press";
|
|
844
|
-
import { clamp } from "@tastic/core";
|
|
857
|
+
import { clamp, useRotation as useRotation4 } from "@tastic/core";
|
|
845
858
|
import { ScrollView as ScrollView4, StyleSheet as StyleSheet10, useWindowDimensions as useWindowDimensions3, View as View10 } from "react-native";
|
|
846
|
-
import { Icon as
|
|
859
|
+
import { Icon as Icon6, Text as Text6 } from "react-native-paper";
|
|
847
860
|
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
848
861
|
var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
|
|
849
862
|
var DEFAULT_SIZE = 48;
|
|
@@ -857,7 +870,9 @@ var SCREEN_MARGIN = 40;
|
|
|
857
870
|
function widthForColumns(columns) {
|
|
858
871
|
return SWATCHES_BORDER_WIDTH * 2 + SWATCHES_PADDING * 2 + SWATCH_SIZE * columns + SWATCHES_GAP * (columns - 1);
|
|
859
872
|
}
|
|
860
|
-
function InlineColorPicker({ id, host, value, onChange, previewValue, swatches = defaultColors, takenValue, allowSwapTaken, dark, align: alignOverride, icon = "palette", tag, labelFontFamily = MONO_FONT, autoDismiss = true, columns, size = DEFAULT_SIZE }) {
|
|
873
|
+
function InlineColorPicker({ id, host, value, onChange, previewValue, swatches = defaultColors, takenValue, allowSwapTaken, dark, align: alignOverride, rotation: rotationOverride, icon = "palette", tag, labelFontFamily = MONO_FONT, autoDismiss = true, columns, size = DEFAULT_SIZE }) {
|
|
874
|
+
const ambientRotation = useRotation4();
|
|
875
|
+
const rotation = rotationOverride ?? ambientRotation;
|
|
861
876
|
const menuBg = dark ? "#000000" : "#FFFFFF";
|
|
862
877
|
const { width: windowWidth } = useWindowDimensions3();
|
|
863
878
|
const autoColumns = clamp(Math.floor((windowWidth - 2 * SCREEN_MARGIN + SWATCHES_GAP) / (SWATCH_SIZE + SWATCHES_GAP)), MIN_COLUMNS, MAX_COLUMNS);
|
|
@@ -866,14 +881,14 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
|
|
|
866
881
|
const swatchRows = Math.ceil(swatches.length / resolvedColumns);
|
|
867
882
|
const swatchesHeight = SWATCHES_BORDER_WIDTH * 2 + SWATCHES_PADDING * 2 + SWATCH_SIZE * swatchRows + SWATCHES_GAP * (swatchRows - 1);
|
|
868
883
|
const open = host.openId === id;
|
|
869
|
-
const { align: autoAlign, maxHeight, measured, triggerRef, verticalAlign } = useAutoAlign(open, swatchesWidth, swatchesHeight);
|
|
884
|
+
const { align: autoAlign, maxHeight, measured, triggerRef, verticalAlign } = useAutoAlign(open, swatchesWidth, swatchesHeight, rotation);
|
|
870
885
|
const align = alignOverride ?? autoAlign;
|
|
871
886
|
const displayFor = previewValue ?? ((hex) => hex);
|
|
872
887
|
const displayValue = displayFor(value);
|
|
873
888
|
const contrastColor = getContrastColor(displayValue);
|
|
874
889
|
const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : 0.4);
|
|
875
890
|
return /* @__PURE__ */ jsxs10(View10, { style: [styles10.anchor, open && styles10.anchorOpen], children: [
|
|
876
|
-
/* @__PURE__ */ jsx12(TouchableRipple3, { onPress: () => host.toggle(id), borderless: true, style: [styles10.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ jsx12(View10, { ref: triggerRef, collapsable: false, style: styles10.triggerMeasure, children: tag ? /* @__PURE__ */ jsx12(Text6, { style: [styles10.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ jsx12(
|
|
891
|
+
/* @__PURE__ */ jsx12(TouchableRipple3, { onPress: () => host.toggle(id), borderless: true, style: [styles10.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ jsx12(View10, { ref: triggerRef, collapsable: false, style: styles10.triggerMeasure, children: tag ? /* @__PURE__ */ jsx12(Text6, { style: [styles10.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ jsx12(Icon6, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
|
|
877
892
|
/* @__PURE__ */ jsx12(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: displayValue, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ jsx12(ScrollView4, { style: [styles10.swatches, { backgroundColor: menuBg, borderColor: displayValue, width: swatchesWidth, maxHeight }], contentContainerStyle: styles10.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
|
|
878
893
|
const selected = swatch.value.toLowerCase() === value.toLowerCase();
|
|
879
894
|
const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
|
|
@@ -889,7 +904,7 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
|
|
|
889
904
|
},
|
|
890
905
|
borderless: true,
|
|
891
906
|
style: [styles10.swatch, { backgroundColor: swatchDisplay }, selected && styles10.swatchSelected, taken && !swappable && styles10.swatchTaken],
|
|
892
|
-
children: selected ? /* @__PURE__ */ jsx12(
|
|
907
|
+
children: selected ? /* @__PURE__ */ jsx12(Icon6, { source: "check", size: 16, color: getContrastColor(swatchDisplay) }) : swappable ? /* @__PURE__ */ jsx12(Icon6, { source: "swap-horizontal", size: 16, color: getContrastColor(swatchDisplay) }) : taken ? /* @__PURE__ */ jsx12(Icon6, { source: "close", size: 16, color: getContrastColor(swatchDisplay) }) : /* @__PURE__ */ jsx12(View10, {})
|
|
893
908
|
},
|
|
894
909
|
swatch.value
|
|
895
910
|
);
|
|
@@ -971,7 +986,7 @@ var styles10 = StyleSheet10.create({
|
|
|
971
986
|
import { getContrastColor as getContrastColor2 } from "@rific/auto-paper";
|
|
972
987
|
import { TouchableRipple as TouchableRipple4 } from "@rific/feedback-press";
|
|
973
988
|
import { ScrollView as ScrollView5, StyleSheet as StyleSheet11, View as View11 } from "react-native";
|
|
974
|
-
import { Icon as
|
|
989
|
+
import { Icon as Icon7, Text as Text7 } from "react-native-paper";
|
|
975
990
|
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
976
991
|
var TRIGGER_HEIGHT = 28;
|
|
977
992
|
var POPOVER_WIDTH = 180;
|
|
@@ -998,13 +1013,13 @@ function LabeledDropdown({ id, host, options, value, onChange, color, dark, alig
|
|
|
998
1013
|
return /* @__PURE__ */ jsxs11(View11, { style: [styles11.anchor, open && styles11.anchorOpen], children: [
|
|
999
1014
|
/* @__PURE__ */ jsx13(TouchableRipple4, { onPress: () => host.toggle(id), style: styles11.trigger, children: /* @__PURE__ */ jsxs11(View11, { ref: triggerRef, collapsable: false, style: styles11.triggerInner, children: [
|
|
1000
1015
|
/* @__PURE__ */ jsx13(Text7, { style: [styles11.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
|
|
1001
|
-
/* @__PURE__ */ jsx13(
|
|
1016
|
+
/* @__PURE__ */ jsx13(Icon7, { source: "menu-down", size: 16, color })
|
|
1002
1017
|
] }) }),
|
|
1003
1018
|
/* @__PURE__ */ jsx13(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ jsx13(ScrollView5, { style: [styles11.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles11.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
|
|
1004
1019
|
const isSelected = option.value === value;
|
|
1005
1020
|
const onColor = getContrastColor2(color);
|
|
1006
1021
|
return /* @__PURE__ */ jsx13(TouchableRipple4, { onPress: () => handleSelect(option), style: [styles11.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ jsxs11(View11, { style: styles11.rowInner, children: [
|
|
1007
|
-
option.icon && /* @__PURE__ */ jsx13(
|
|
1022
|
+
option.icon && /* @__PURE__ */ jsx13(Icon7, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
|
|
1008
1023
|
/* @__PURE__ */ jsx13(Text7, { style: [styles11.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
|
|
1009
1024
|
] }) }, option.value);
|
|
1010
1025
|
}) }) })
|
|
@@ -1062,6 +1077,11 @@ var styles11 = StyleSheet11.create({
|
|
|
1062
1077
|
}
|
|
1063
1078
|
});
|
|
1064
1079
|
|
|
1080
|
+
// src/loadSkiaWeb.ts
|
|
1081
|
+
function loadSkiaWeb() {
|
|
1082
|
+
return Promise.resolve();
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1065
1085
|
// src/PressAwayOverlay.tsx
|
|
1066
1086
|
import { Pressable, StyleSheet as StyleSheet12 } from "react-native";
|
|
1067
1087
|
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
@@ -1207,6 +1227,7 @@ export {
|
|
|
1207
1227
|
ReadyButton,
|
|
1208
1228
|
SectionedDropdown,
|
|
1209
1229
|
SharedActionBand,
|
|
1230
|
+
SkiaGate,
|
|
1210
1231
|
StatRow,
|
|
1211
1232
|
StatSection,
|
|
1212
1233
|
TriggerGaugeHost,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tastic/hud",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Visual component kit for local-multiplayer React Native games: inline (non-modal) popovers, dropdowns, color pickers, gauges, ready buttons, and dialogs, built to stay scoped to one player's own zone on a shared screen",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -33,6 +33,13 @@
|
|
|
33
33
|
"types": "./dist/index.d.ts",
|
|
34
34
|
"import": "./dist/index.mjs",
|
|
35
35
|
"require": "./dist/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./skia-gate": {
|
|
38
|
+
"react-native": "./src/SkiaGate.tsx",
|
|
39
|
+
"browser": "./src/SkiaGate.web.tsx",
|
|
40
|
+
"types": "./dist/SkiaGate.d.ts",
|
|
41
|
+
"import": "./dist/SkiaGate.mjs",
|
|
42
|
+
"require": "./dist/SkiaGate.js"
|
|
36
43
|
}
|
|
37
44
|
},
|
|
38
45
|
"main": "dist/index.js",
|
|
@@ -63,14 +70,14 @@
|
|
|
63
70
|
},
|
|
64
71
|
"prettier": "@infinitetoken/eslint-config/prettier",
|
|
65
72
|
"devDependencies": {
|
|
66
|
-
"@infinitetoken/eslint-config": "0.2.0",
|
|
67
|
-
"@infinitetoken/jest-config": "^0.
|
|
73
|
+
"@infinitetoken/eslint-config": "^0.2.0",
|
|
74
|
+
"@infinitetoken/jest-config": "^0.3.0",
|
|
68
75
|
"@infinitetoken/tsconfig": "^0.4.1",
|
|
69
76
|
"@rific/auto-paper": "^0.10.3",
|
|
70
77
|
"@rific/feedback-press": "^0.10.8",
|
|
71
|
-
"@rific/updater": "^0.
|
|
78
|
+
"@rific/updater": "^0.6.0",
|
|
72
79
|
"@shopify/react-native-skia": "^2.6.2",
|
|
73
|
-
"@tastic/core": "^0.
|
|
80
|
+
"@tastic/core": "^0.3.0",
|
|
74
81
|
"@testing-library/dom": "^10.4.1",
|
|
75
82
|
"@testing-library/react": "^16.3.2",
|
|
76
83
|
"@types/jest": "^30.0.0",
|
|
@@ -94,7 +101,7 @@
|
|
|
94
101
|
"@rific/feedback-press": ">=0.10.0",
|
|
95
102
|
"@rific/updater": ">=0.4.0",
|
|
96
103
|
"@shopify/react-native-skia": ">=1.5.0",
|
|
97
|
-
"@tastic/core": ">=0.
|
|
104
|
+
"@tastic/core": ">=0.3.0",
|
|
98
105
|
"react": ">=19.0.0",
|
|
99
106
|
"react-native": ">=0.76.0",
|
|
100
107
|
"react-native-paper": ">=5.0.0",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AutoAppearancePicker, Dialog, useAutoPaperTheme } from '@rific/auto-paper'
|
|
2
2
|
import { Button, SoundContext, TouchableRipple, useHapticSettings, useSoundSettings, useVibration } from '@rific/feedback-press'
|
|
3
3
|
import { useUpdater } from '@rific/updater'
|
|
4
|
-
import { useIsTouchPrimaryDevice } from '@tastic/core'
|
|
4
|
+
import { useIsTouchPrimaryDevice, useRotation } from '@tastic/core'
|
|
5
5
|
import { ReactNode, useContext, useState } from 'react'
|
|
6
6
|
import { Platform, ScrollView, StyleSheet, View } from 'react-native'
|
|
7
7
|
import { Icon, Portal, Text } from 'react-native-paper'
|
|
@@ -26,10 +26,11 @@ function SettingIcon({ source, color, containerColor }: SettingIconProps) {
|
|
|
26
26
|
export interface BaseSettingsDialogProps {
|
|
27
27
|
visible: boolean
|
|
28
28
|
onDismiss: () => void
|
|
29
|
-
//
|
|
30
|
-
// app-wide modal with no per-player zone to match (unlike an in-game round-over
|
|
31
|
-
// just rotates its own content in place
|
|
32
|
-
//
|
|
29
|
+
// Explicit override for the live physical-hold rotation (see @tastic/core's useRotation) — this is
|
|
30
|
+
// a centered, app-wide modal with no per-player zone to match (unlike an in-game round-over
|
|
31
|
+
// dialog), so it just rotates its own content in place. Defaults to a live ambient read via
|
|
32
|
+
// useRotation() when omitted; pass this only when a caller's own reading needs to differ from the
|
|
33
|
+
// ambient one (rare — most callers should simply omit it).
|
|
33
34
|
rotation?: number
|
|
34
35
|
// Each app's own OTA version (e.g. release.otaVersion) — not something this package can read
|
|
35
36
|
// itself, since every consuming app tracks its own release.ts independently. Accepts a number too
|
|
@@ -76,8 +77,10 @@ export interface BaseSettingsDialogProps {
|
|
|
76
77
|
// is the seam for whatever isn't shared: a game with its own board/difficulty settings (or a stats
|
|
77
78
|
// backup flow, or anything else genuinely specific to it) renders those below Appearance, not by
|
|
78
79
|
// forking this component.
|
|
79
|
-
export function BaseSettingsDialog({ visible, onDismiss, rotation
|
|
80
|
+
export function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, children }: BaseSettingsDialogProps) {
|
|
80
81
|
const { dark, colors } = useAutoPaperTheme()
|
|
82
|
+
const ambientRotation = useRotation()
|
|
83
|
+
const rotation = rotationOverride ?? ambientRotation
|
|
81
84
|
// Also gates the Lock Orientation row below (alongside showLockOrientation itself) — true
|
|
82
85
|
// unconditionally on native (see the hook's own doc), so this only actually excludes a
|
|
83
86
|
// desktop/laptop browser, where a mouse-driven window has no physical orientation to lock
|