@tastic/hud 0.21.0 → 0.22.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 +10 -1
- package/dist/chrome/index.d.mts +15 -3
- package/dist/chrome/index.d.ts +15 -3
- package/dist/chrome/index.js +54 -41
- package/dist/chrome/index.mjs +7 -3
- package/dist/{chunk-UOA35OWJ.mjs → chunk-N7A7W72S.mjs} +35 -26
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +61 -48
- package/dist/index.mjs +6 -2
- package/package.json +1 -1
- package/src/AchievementCatalogSection.tsx +7 -6
- package/src/AchievementRow.tsx +13 -13
- package/src/ActivityStatSection.tsx +10 -5
- package/src/StatRow.tsx +10 -11
- package/src/StatSection.tsx +14 -14
- package/src/chrome/index.ts +1 -0
- package/src/index.ts +1 -0
- package/src/wellSurface.ts +30 -0
package/dist/index.js
CHANGED
|
@@ -200,6 +200,7 @@ __export(index_exports, {
|
|
|
200
200
|
StatSection: () => StatSection,
|
|
201
201
|
TriggerGaugeHost: () => TriggerGaugeHost,
|
|
202
202
|
UpdateDialog: () => UpdateDialog,
|
|
203
|
+
WELL_SHADOW: () => WELL_SHADOW,
|
|
203
204
|
getColorPopoverId: () => getColorPopoverId,
|
|
204
205
|
getCpuDifficultyPopoverId: () => getCpuDifficultyPopoverId,
|
|
205
206
|
getInlineColorPickerContentSize: () => getInlineColorPickerContentSize,
|
|
@@ -215,6 +216,7 @@ __export(index_exports, {
|
|
|
215
216
|
useOverlayCardColors: () => useOverlayCardColors,
|
|
216
217
|
usePopoverHost: () => usePopoverHost,
|
|
217
218
|
useQuitConfirmation: () => useQuitConfirmation,
|
|
219
|
+
useWellColors: () => useWellColors,
|
|
218
220
|
useZoneClampedAlign: () => useZoneClampedAlign
|
|
219
221
|
});
|
|
220
222
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -226,16 +228,25 @@ var import_react_native3 = require("react-native");
|
|
|
226
228
|
var import_react_native_paper2 = require("react-native-paper");
|
|
227
229
|
|
|
228
230
|
// src/AchievementRow.tsx
|
|
229
|
-
var import_auto_paper = require("@rific/auto-paper");
|
|
230
231
|
var import_react_native = require("react-native");
|
|
231
232
|
var import_react_native_paper = require("react-native-paper");
|
|
233
|
+
|
|
234
|
+
// src/wellSurface.ts
|
|
235
|
+
var import_auto_paper = require("@rific/auto-paper");
|
|
236
|
+
var WELL_SHADOW = "0px 1px 2px rgba(0, 0, 0, 0.3), 0px 1px 3px 1px rgba(0, 0, 0, 0.15)";
|
|
237
|
+
function useWellColors() {
|
|
238
|
+
const { colors } = (0, import_auto_paper.useAutoPaperTheme)();
|
|
239
|
+
return { bg: colors.surface, fg: colors.onSurface, fgMuted: colors.onSurfaceVariant };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// src/AchievementRow.tsx
|
|
232
243
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
233
244
|
var LOCKED_BADGE_COLOR = "rgba(128,128,128,0.3)";
|
|
234
245
|
function AchievementRow({ icon, title, description, badgeColor, unlockedLabel, checkColor, progress, deviceMarker, fg: fgOverride, fgMuted: fgMutedOverride, sectionBg: sectionBgOverride }) {
|
|
235
|
-
const
|
|
236
|
-
const fg = fgOverride ??
|
|
237
|
-
const fgMuted = fgMutedOverride ??
|
|
238
|
-
const sectionBg = sectionBgOverride ??
|
|
246
|
+
const well = useWellColors();
|
|
247
|
+
const fg = fgOverride ?? well.fg;
|
|
248
|
+
const fgMuted = fgMutedOverride ?? well.fgMuted;
|
|
249
|
+
const sectionBg = sectionBgOverride ?? well.bg;
|
|
239
250
|
const unlocked = unlockedLabel !== void 0;
|
|
240
251
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: [styles.row, { backgroundColor: sectionBg }], children: [
|
|
241
252
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: [styles.badge, { backgroundColor: badgeColor }], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Icon, { source: icon, size: 20, color: unlocked ? "#000000" : fgMuted }) }),
|
|
@@ -278,6 +289,7 @@ var styles = import_react_native.StyleSheet.create({
|
|
|
278
289
|
row: {
|
|
279
290
|
alignItems: "center",
|
|
280
291
|
borderRadius: 12,
|
|
292
|
+
boxShadow: WELL_SHADOW,
|
|
281
293
|
flexDirection: "row",
|
|
282
294
|
gap: 12,
|
|
283
295
|
padding: 12
|
|
@@ -299,13 +311,13 @@ var MONO_FONT = import_react_native2.Platform.select({ ios: "Menlo", android: "m
|
|
|
299
311
|
|
|
300
312
|
// src/AchievementCatalogSection.tsx
|
|
301
313
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
302
|
-
function AchievementCatalogSection({ rows, label = "ALL ACHIEVEMENTS", fg, fgMuted: fgMutedOverride }) {
|
|
314
|
+
function AchievementCatalogSection({ rows, label = "ALL ACHIEVEMENTS", fg, fgMuted: fgMutedOverride, sectionBg }) {
|
|
303
315
|
const { dark } = (0, import_auto_paper2.useAutoPaperTheme)();
|
|
304
316
|
const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
|
|
305
317
|
const tierAccent = (tierColor) => dark ? tierColor : (0, import_auto_paper2.getBlendedColor)("#000000", tierColor, 0.5);
|
|
306
318
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react.Fragment, { children: [
|
|
307
319
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.Text, { variant: "labelMedium", style: [styles2.label, { color: fgMuted, fontFamily: MONO_FONT }], children: label }),
|
|
308
|
-
rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AchievementRow, { icon: row.icon, title: row.title, description: row.description, badgeColor: row.unlockedAt !== void 0 ? row.tierColor : LOCKED_BADGE_COLOR, checkColor: tierAccent(row.tierColor), unlockedLabel: row.unlockedLabel, progress: row.progress, deviceMarker: row.deviceMarker, fg, fgMuted: fgMutedOverride }, row.id))
|
|
320
|
+
rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AchievementRow, { icon: row.icon, title: row.title, description: row.description, badgeColor: row.unlockedAt !== void 0 ? row.tierColor : LOCKED_BADGE_COLOR, checkColor: tierAccent(row.tierColor), unlockedLabel: row.unlockedLabel, progress: row.progress, deviceMarker: row.deviceMarker, fg, fgMuted: fgMutedOverride, sectionBg }, row.id))
|
|
309
321
|
] });
|
|
310
322
|
}
|
|
311
323
|
var styles2 = import_react_native3.StyleSheet.create({
|
|
@@ -360,14 +372,13 @@ var styles3 = import_react_native4.StyleSheet.create({
|
|
|
360
372
|
});
|
|
361
373
|
|
|
362
374
|
// src/StatRow.tsx
|
|
363
|
-
var import_auto_paper4 = require("@rific/auto-paper");
|
|
364
375
|
var import_react_native5 = require("react-native");
|
|
365
376
|
var import_react_native_paper4 = require("react-native-paper");
|
|
366
377
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
367
378
|
function StatRow({ label, value, fg: fgOverride, fgMuted: fgMutedOverride }) {
|
|
368
|
-
const
|
|
369
|
-
const fg = fgOverride ??
|
|
370
|
-
const fgMuted = fgMutedOverride ??
|
|
379
|
+
const well = useWellColors();
|
|
380
|
+
const fg = fgOverride ?? well.fg;
|
|
381
|
+
const fgMuted = fgMutedOverride ?? well.fgMuted;
|
|
371
382
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native5.View, { style: styles4.row, children: [
|
|
372
383
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper4.Text, { variant: "bodyMedium", style: { color: fgMuted }, children: label }),
|
|
373
384
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper4.Text, { variant: "bodyMedium", style: [styles4.value, { color: fg }], children: value })
|
|
@@ -385,14 +396,13 @@ var styles4 = import_react_native5.StyleSheet.create({
|
|
|
385
396
|
});
|
|
386
397
|
|
|
387
398
|
// src/StatSection.tsx
|
|
388
|
-
var import_auto_paper5 = require("@rific/auto-paper");
|
|
389
399
|
var import_react_native6 = require("react-native");
|
|
390
400
|
var import_react_native_paper5 = require("react-native-paper");
|
|
391
401
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
392
402
|
function StatSection({ label, fg: fgOverride, sectionBg: sectionBgOverride, children }) {
|
|
393
|
-
const
|
|
394
|
-
const fg = fgOverride ??
|
|
395
|
-
const sectionBg = sectionBgOverride ??
|
|
403
|
+
const well = useWellColors();
|
|
404
|
+
const fg = fgOverride ?? well.fg;
|
|
405
|
+
const sectionBg = sectionBgOverride ?? well.bg;
|
|
396
406
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native6.View, { style: [styles5.section, { backgroundColor: sectionBg }], children: [
|
|
397
407
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_paper5.Text, { variant: "labelMedium", style: [styles5.label, { color: fg }], children: label }),
|
|
398
408
|
children
|
|
@@ -409,6 +419,7 @@ var styles5 = import_react_native6.StyleSheet.create({
|
|
|
409
419
|
},
|
|
410
420
|
section: {
|
|
411
421
|
borderRadius: 12,
|
|
422
|
+
boxShadow: WELL_SHADOW,
|
|
412
423
|
gap: 8,
|
|
413
424
|
padding: 16
|
|
414
425
|
}
|
|
@@ -416,16 +427,16 @@ var styles5 = import_react_native6.StyleSheet.create({
|
|
|
416
427
|
|
|
417
428
|
// src/ActivityStatSection.tsx
|
|
418
429
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
419
|
-
function ActivityStatSection({ stats }) {
|
|
420
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(StatSection, { label: "ACTIVITY", children: [
|
|
421
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRow, { label: "Days Played", value: String(stats.distinctDaysPlayed) }),
|
|
422
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRow, { label: "Day Streak", value: String(stats.currentDayStreak) }),
|
|
423
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRow, { label: "Best Day Streak", value: String(stats.bestDayStreak) })
|
|
430
|
+
function ActivityStatSection({ stats, fg, fgMuted, sectionBg }) {
|
|
431
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(StatSection, { label: "ACTIVITY", fg, sectionBg, children: [
|
|
432
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRow, { label: "Days Played", value: String(stats.distinctDaysPlayed), fg, fgMuted }),
|
|
433
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRow, { label: "Day Streak", value: String(stats.currentDayStreak), fg, fgMuted }),
|
|
434
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRow, { label: "Best Day Streak", value: String(stats.bestDayStreak), fg, fgMuted })
|
|
424
435
|
] });
|
|
425
436
|
}
|
|
426
437
|
|
|
427
438
|
// src/BaseSettingsDialog.tsx
|
|
428
|
-
var
|
|
439
|
+
var import_auto_paper7 = require("@rific/auto-paper");
|
|
429
440
|
var import_feedback_press2 = require("@rific/feedback-press");
|
|
430
441
|
var import_updater = require("@rific/updater");
|
|
431
442
|
var import_core4 = require("@tastic/core");
|
|
@@ -435,7 +446,7 @@ var import_react_native_paper9 = require("react-native-paper");
|
|
|
435
446
|
var import_react_native_safe_area_context2 = require("react-native-safe-area-context");
|
|
436
447
|
|
|
437
448
|
// src/ConfirmDialog.tsx
|
|
438
|
-
var
|
|
449
|
+
var import_auto_paper6 = require("@rific/auto-paper");
|
|
439
450
|
var import_feedback_press = require("@rific/feedback-press");
|
|
440
451
|
var import_core2 = require("@tastic/core");
|
|
441
452
|
var import_react3 = require("react");
|
|
@@ -443,7 +454,7 @@ var import_react_native11 = require("react-native");
|
|
|
443
454
|
var import_react_native_paper7 = require("react-native-paper");
|
|
444
455
|
|
|
445
456
|
// src/DialogHeader.tsx
|
|
446
|
-
var
|
|
457
|
+
var import_auto_paper4 = require("@rific/auto-paper");
|
|
447
458
|
var import_react_native7 = require("react-native");
|
|
448
459
|
var import_react_native_paper6 = require("react-native-paper");
|
|
449
460
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
@@ -452,7 +463,7 @@ function isCompactDialogHeight(height) {
|
|
|
452
463
|
return height < COMPACT_DIALOG_HEIGHT;
|
|
453
464
|
}
|
|
454
465
|
function DialogHeader({ icon, title, color, compact = false, style }) {
|
|
455
|
-
const { colors } = (0,
|
|
466
|
+
const { colors } = (0, import_auto_paper4.useAutoPaperTheme)();
|
|
456
467
|
const accent = color ?? colors.primary;
|
|
457
468
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native7.View, { style: [compact ? styles6.compact : styles6.full, style], children: [
|
|
458
469
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper6.Icon, { source: icon, size: compact ? COMPACT_ICON_SIZE : ICON_SIZE, color: accent }),
|
|
@@ -549,11 +560,11 @@ var overlayActionStyles = import_react_native8.StyleSheet.create({
|
|
|
549
560
|
});
|
|
550
561
|
|
|
551
562
|
// src/OverlaySurface.tsx
|
|
552
|
-
var
|
|
563
|
+
var import_auto_paper5 = require("@rific/auto-paper");
|
|
553
564
|
var import_react_native9 = require("react-native");
|
|
554
565
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
555
566
|
function useOverlayCardColors() {
|
|
556
|
-
const { colors } = (0,
|
|
567
|
+
const { colors } = (0, import_auto_paper5.useAutoPaperTheme)();
|
|
557
568
|
return { fg: colors.onSurface, fgMuted: colors.onSurfaceVariant, cardBorder: colors.outlineVariant, backdrop: colors.backdrop };
|
|
558
569
|
}
|
|
559
570
|
function OverlayBackdrop({ children, style, onLayout }) {
|
|
@@ -561,9 +572,9 @@ function OverlayBackdrop({ children, style, onLayout }) {
|
|
|
561
572
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native9.View, { onLayout, style: [overlayStyles.overlay, { backgroundColor: backdrop }, style], children });
|
|
562
573
|
}
|
|
563
574
|
function OverlayCard({ children, style, ...rest }) {
|
|
564
|
-
const { isV3, roundness } = (0,
|
|
565
|
-
const blur = (0,
|
|
566
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
575
|
+
const { isV3, roundness } = (0, import_auto_paper5.useAutoPaperTheme)();
|
|
576
|
+
const blur = (0, import_auto_paper5.useBlur)();
|
|
577
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_auto_paper5.BlurView, { blur, style: [overlayStyles.card, styles7.card, { borderRadius: (isV3 ? 7 : 1) * roundness }, style], ...rest, children });
|
|
567
578
|
}
|
|
568
579
|
var styles7 = import_react_native9.StyleSheet.create({
|
|
569
580
|
card: { overflow: "hidden" }
|
|
@@ -589,7 +600,7 @@ function useCompactDialog(rotation = 0) {
|
|
|
589
600
|
// src/ConfirmDialog.tsx
|
|
590
601
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
591
602
|
function ConfirmDialog({ visible, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, rotation = 0, icon = "help-circle-outline", destructive = false }) {
|
|
592
|
-
const { colors } = (0,
|
|
603
|
+
const { colors } = (0, import_auto_paper6.useAutoPaperTheme)();
|
|
593
604
|
const { fg } = useOverlayCardColors();
|
|
594
605
|
const { compact, onLayout } = useCompactDialog(rotation);
|
|
595
606
|
const onCancelRef = (0, import_react3.useRef)(onCancel);
|
|
@@ -694,7 +705,7 @@ function SettingIcon({ source, color, containerColor }) {
|
|
|
694
705
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native13.View, { style: [styles10.iconBadge, { backgroundColor: containerColor }], children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native_paper9.Icon, { source, size: 18, color }) });
|
|
695
706
|
}
|
|
696
707
|
function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, version, lockOrientation, onLockOrientationChange, deferBottomEdgeGestures, onDeferBottomEdgeGestures, keepAwake, onKeepAwakeChange, hideSound = false, hideHaptics = false, hideAppearance = false, hideUpdateCheck = false, onUpdateError, onShowHowToPlay, children }) {
|
|
697
|
-
const { colors } = (0,
|
|
708
|
+
const { colors } = (0, import_auto_paper7.useAutoPaperTheme)();
|
|
698
709
|
const ambientRotation = (0, import_core4.useRotation)();
|
|
699
710
|
const rotation = rotationOverride ?? ambientRotation;
|
|
700
711
|
const window = (0, import_react_native13.useWindowDimensions)();
|
|
@@ -707,8 +718,8 @@ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, ve
|
|
|
707
718
|
const { settings: soundSettings, set: setSoundSettings } = (0, import_feedback_press2.useSoundSettings)();
|
|
708
719
|
const sound = (0, import_react6.useContext)(import_feedback_press2.SoundContext);
|
|
709
720
|
const { forceShort: forceHapticFeedback } = (0, import_feedback_press2.useVibration)();
|
|
710
|
-
const { settings: themeSettings, set: setThemeSettings } = (0,
|
|
711
|
-
const expoBlur = (0,
|
|
721
|
+
const { settings: themeSettings, set: setThemeSettings } = (0, import_auto_paper7.useThemeSettings)();
|
|
722
|
+
const expoBlur = (0, import_auto_paper7.useBlurModule)();
|
|
712
723
|
const [infoMessage, setInfoMessage] = (0, import_react6.useState)(null);
|
|
713
724
|
const { manifest: updateManifest, onConfirm: onUpdateConfirm, respond: respondToUpdateConfirm } = useUpdateConfirmBridge();
|
|
714
725
|
const { check, checking, updateReady } = (0, import_updater.useUpdater)({
|
|
@@ -731,8 +742,8 @@ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, ve
|
|
|
731
742
|
const showUpdateCheck = import_react_native13.Platform.OS !== "web" && !hideUpdateCheck;
|
|
732
743
|
if (!opened) return null;
|
|
733
744
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native_paper9.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(RotatedFrame, { rotation, rotatedSafeArea: true, portalHost: true, passThrough: true, children: [
|
|
734
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
735
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
745
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_auto_paper7.Dialog, { visible, onDismiss, style: [styles10.dialog, { width: sheetWidth }], children: [
|
|
746
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_auto_paper7.Dialog.ScrollArea, { style: styles10.scrollArea, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_auto_paper7.FadingScrollView, { contentContainerStyle: styles10.scrollContent, showsVerticalScrollIndicator: false, children: [
|
|
736
747
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(DialogHeader, { icon: "cog", title: "Settings", compact: compactHeader }),
|
|
737
748
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native13.View, { style: styles10.content, children: [
|
|
738
749
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native13.View, { style: styles10.toggleGroup, children: [
|
|
@@ -814,7 +825,7 @@ function BaseSettingsDialog({ visible, onDismiss, rotation: rotationOverride, ve
|
|
|
814
825
|
] }),
|
|
815
826
|
showAppearance && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native13.View, { style: styles10.section, children: [
|
|
816
827
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native_paper9.Text, { variant: "labelMedium", style: [styles10.sectionLabel, { color: colors.onSurfaceVariant }], children: "APPEARANCE" }),
|
|
817
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
828
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_auto_paper7.AutoAppearancePicker, { showLabels: false }),
|
|
818
829
|
showBlur && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => setThemeSettings({ blur: !themeSettings.blur }), style: styles10.toggleButton, accessibilityLabel: `Blur ${themeSettings.blur ? "on" : "off"}`, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native13.View, { style: styles10.toggleContent, children: [
|
|
819
830
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SettingIcon, { source: themeSettings.blur ? "blur" : "blur-off", color: themeSettings.blur ? colors.tertiary : colors.onSurfaceVariant, containerColor: themeSettings.blur ? colors.tertiaryContainer : colors.surfaceVariant }),
|
|
820
831
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native13.View, { style: styles10.flexShrink, children: [
|
|
@@ -925,7 +936,7 @@ var styles10 = import_react_native13.StyleSheet.create({
|
|
|
925
936
|
});
|
|
926
937
|
|
|
927
938
|
// src/BaseStatsScreen.tsx
|
|
928
|
-
var
|
|
939
|
+
var import_auto_paper8 = require("@rific/auto-paper");
|
|
929
940
|
var import_feedback_press3 = require("@rific/feedback-press");
|
|
930
941
|
var import_core5 = require("@tastic/core");
|
|
931
942
|
var import_react7 = require("react");
|
|
@@ -933,7 +944,7 @@ var import_react_native14 = require("react-native");
|
|
|
933
944
|
var import_react_native_paper10 = require("react-native-paper");
|
|
934
945
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
935
946
|
function BaseStatsScreen({ rotation: rotationOverride, title = "Achievements", titleVariant = "headlineSmall", onBack, backLabel = "Back", hitSlop, 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 }) {
|
|
936
|
-
const { dark, colors } = (0,
|
|
947
|
+
const { dark, colors } = (0, import_auto_paper8.useAutoPaperTheme)();
|
|
937
948
|
const ambientRotation = (0, import_core5.useRotation)();
|
|
938
949
|
const rotation = rotationOverride ?? ambientRotation;
|
|
939
950
|
const [confirmResetVisible, setConfirmResetVisible] = (0, import_react7.useState)(false);
|
|
@@ -945,7 +956,7 @@ function BaseStatsScreen({ rotation: rotationOverride, title = "Achievements", t
|
|
|
945
956
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_feedback_press3.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, hitSlop, onPress: onBack, accessibilityLabel: backLabel }),
|
|
946
957
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_paper10.Text, { variant: titleVariant, style: [styles11.title, { color: fg }], children: title })
|
|
947
958
|
] }),
|
|
948
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
959
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_auto_paper8.FadingScrollView, { style: styles11.scrollView, contentContainerStyle: [styles11.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, children: [
|
|
949
960
|
children,
|
|
950
961
|
showReset && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_feedback_press3.Button, { mode: "contained", onPress: () => setConfirmResetVisible(true), buttonColor: colors.danger, textColor: colors.onDanger, style: styles11.resetButton, children: resetLabel })
|
|
951
962
|
] }),
|
|
@@ -1028,7 +1039,7 @@ var import_core9 = require("@tastic/core");
|
|
|
1028
1039
|
var import_react_native20 = require("react-native");
|
|
1029
1040
|
|
|
1030
1041
|
// src/SectionedDropdown.tsx
|
|
1031
|
-
var
|
|
1042
|
+
var import_auto_paper9 = require("@rific/auto-paper");
|
|
1032
1043
|
var import_feedback_press4 = require("@rific/feedback-press");
|
|
1033
1044
|
var import_core8 = require("@tastic/core");
|
|
1034
1045
|
var import_react_native19 = require("react-native");
|
|
@@ -1189,8 +1200,8 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
|
|
|
1189
1200
|
const ambientRotation = (0, import_core8.useRotation)();
|
|
1190
1201
|
const rotation = rotationOverride ?? ambientRotation;
|
|
1191
1202
|
const menuBg = dark ? "#000000" : "#FFFFFF";
|
|
1192
|
-
const menuBorderColor = (0,
|
|
1193
|
-
const resolvedOnAccentColor = onAccentColor ?? (0,
|
|
1203
|
+
const menuBorderColor = (0, import_auto_paper9.getBlendedColor)(mutedColor, menuBg, 0.5);
|
|
1204
|
+
const resolvedOnAccentColor = onAccentColor ?? (0, import_auto_paper9.getColorRoles)(accentColor, menuBg).onColor;
|
|
1194
1205
|
const fg = dark ? "#FFFFFF" : "#000000";
|
|
1195
1206
|
const open = host.openId === id;
|
|
1196
1207
|
const estimatedHeight = MENU_VERTICAL_PADDING + sections.reduce((sum, section, sectionIndex) => {
|
|
@@ -1431,7 +1442,7 @@ var styles17 = import_react_native22.StyleSheet.create({
|
|
|
1431
1442
|
});
|
|
1432
1443
|
|
|
1433
1444
|
// src/InlineColorPicker.tsx
|
|
1434
|
-
var
|
|
1445
|
+
var import_auto_paper10 = require("@rific/auto-paper");
|
|
1435
1446
|
var import_feedback_press6 = require("@rific/feedback-press");
|
|
1436
1447
|
var import_core10 = require("@tastic/core");
|
|
1437
1448
|
var import_react_native23 = require("react-native");
|
|
@@ -1457,7 +1468,7 @@ function getInlineColorPickerContentSize(swatchCount, windowWidth, columns) {
|
|
|
1457
1468
|
const height = SWATCHES_BORDER_WIDTH * 2 + SWATCHES_PADDING * 2 + SWATCH_SIZE * rows + SWATCHES_GAP * (rows - 1);
|
|
1458
1469
|
return { width, height };
|
|
1459
1470
|
}
|
|
1460
|
-
function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
|
|
1471
|
+
function InlineColorPicker({ id, host, value, onChange, previewValue, swatches = import_auto_paper10.defaultColors, takenValue, allowSwapTaken, dark, align: forcedAlign, alignOverride, rotation: rotationOverride, icon = "palette", tag, labelFontFamily = MONO_FONT, autoDismiss = true, columns, size = DEFAULT_SIZE }) {
|
|
1461
1472
|
const ambientRotation = (0, import_core10.useRotation)();
|
|
1462
1473
|
const rotation = rotationOverride ?? ambientRotation;
|
|
1463
1474
|
const menuBg = dark ? "#000000" : "#FFFFFF";
|
|
@@ -1470,7 +1481,7 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
|
|
|
1470
1481
|
const align = forcedAlign ?? placement.align;
|
|
1471
1482
|
const displayFor = previewValue ?? ((hex) => hex);
|
|
1472
1483
|
const displayValue = displayFor(value);
|
|
1473
|
-
const contrastColor = (0,
|
|
1484
|
+
const contrastColor = (0, import_auto_paper10.getContrastColor)(displayValue);
|
|
1474
1485
|
const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : 0.4);
|
|
1475
1486
|
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react_native23.View, { style: [styles18.anchor, open && styles18.anchorOpen], children: [
|
|
1476
1487
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_feedback_press6.TouchableRipple, { onPress: () => host.toggle(id), borderless: true, style: [styles18.trigger, { backgroundColor: displayValue, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_native23.View, { ref: triggerRef, collapsable: false, style: styles18.triggerMeasure, children: tag ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_native_paper13.Text, { style: [styles18.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_native_paper13.Icon, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
|
|
@@ -1489,7 +1500,7 @@ function InlineColorPicker({ id, host, value, onChange, previewValue, swatches =
|
|
|
1489
1500
|
},
|
|
1490
1501
|
borderless: true,
|
|
1491
1502
|
style: [styles18.swatch, { backgroundColor: swatchDisplay }, selected && styles18.swatchSelected, taken && !swappable && styles18.swatchTaken],
|
|
1492
|
-
children: selected ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_native_paper13.Icon, { source: "check", size: 16, color: (0,
|
|
1503
|
+
children: selected ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_native_paper13.Icon, { source: "check", size: 16, color: (0, import_auto_paper10.getContrastColor)(swatchDisplay) }) : swappable ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_native_paper13.Icon, { source: "swap-horizontal", size: 16, color: (0, import_auto_paper10.getContrastColor)(swatchDisplay) }) : taken ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_native_paper13.Icon, { source: "close", size: 16, color: (0, import_auto_paper10.getContrastColor)(swatchDisplay) }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_native23.View, {})
|
|
1493
1504
|
},
|
|
1494
1505
|
swatch.value
|
|
1495
1506
|
);
|
|
@@ -1568,7 +1579,7 @@ var styles18 = import_react_native23.StyleSheet.create({
|
|
|
1568
1579
|
});
|
|
1569
1580
|
|
|
1570
1581
|
// src/LabeledDropdown.tsx
|
|
1571
|
-
var
|
|
1582
|
+
var import_auto_paper11 = require("@rific/auto-paper");
|
|
1572
1583
|
var import_feedback_press7 = require("@rific/feedback-press");
|
|
1573
1584
|
var import_react_native24 = require("react-native");
|
|
1574
1585
|
var import_react_native_paper14 = require("react-native-paper");
|
|
@@ -1602,7 +1613,7 @@ function LabeledDropdown({ id, host, options, value, onChange, color, dark, alig
|
|
|
1602
1613
|
] }) }),
|
|
1603
1614
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_native24.ScrollView, { style: [styles19.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles19.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
|
|
1604
1615
|
const isSelected = option.value === value;
|
|
1605
|
-
const onColor = (0,
|
|
1616
|
+
const onColor = (0, import_auto_paper11.getContrastColor)(color);
|
|
1606
1617
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_feedback_press7.TouchableRipple, { onPress: () => handleSelect(option), style: [styles19.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react_native24.View, { style: styles19.rowInner, children: [
|
|
1607
1618
|
option.icon && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_native_paper14.Icon, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
|
|
1608
1619
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_native_paper14.Text, { style: [styles19.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
|
|
@@ -1942,6 +1953,7 @@ function useQuitConfirmation(hasProgress, onConfirmedBack) {
|
|
|
1942
1953
|
StatSection,
|
|
1943
1954
|
TriggerGaugeHost,
|
|
1944
1955
|
UpdateDialog,
|
|
1956
|
+
WELL_SHADOW,
|
|
1945
1957
|
getColorPopoverId,
|
|
1946
1958
|
getCpuDifficultyPopoverId,
|
|
1947
1959
|
getInlineColorPickerContentSize,
|
|
@@ -1957,5 +1969,6 @@ function useQuitConfirmation(hasProgress, onConfirmedBack) {
|
|
|
1957
1969
|
useOverlayCardColors,
|
|
1958
1970
|
usePopoverHost,
|
|
1959
1971
|
useQuitConfirmation,
|
|
1972
|
+
useWellColors,
|
|
1960
1973
|
useZoneClampedAlign
|
|
1961
1974
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -17,9 +17,11 @@ import {
|
|
|
17
17
|
StatRow,
|
|
18
18
|
StatSection,
|
|
19
19
|
UpdateDialog,
|
|
20
|
+
WELL_SHADOW,
|
|
20
21
|
getStaggeredWordDuration,
|
|
21
|
-
useCompactDialog
|
|
22
|
-
|
|
22
|
+
useCompactDialog,
|
|
23
|
+
useWellColors
|
|
24
|
+
} from "./chunk-N7A7W72S.mjs";
|
|
23
25
|
import {
|
|
24
26
|
COMPACT_DIALOG_HEIGHT,
|
|
25
27
|
DialogHeader,
|
|
@@ -915,6 +917,7 @@ export {
|
|
|
915
917
|
StatSection,
|
|
916
918
|
TriggerGaugeHost,
|
|
917
919
|
UpdateDialog,
|
|
920
|
+
WELL_SHADOW,
|
|
918
921
|
getColorPopoverId,
|
|
919
922
|
getCpuDifficultyPopoverId,
|
|
920
923
|
getInlineColorPickerContentSize,
|
|
@@ -930,5 +933,6 @@ export {
|
|
|
930
933
|
useOverlayCardColors,
|
|
931
934
|
usePopoverHost,
|
|
932
935
|
useQuitConfirmation,
|
|
936
|
+
useWellColors,
|
|
933
937
|
useZoneClampedAlign
|
|
934
938
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tastic/hud",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
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",
|
|
@@ -15,12 +15,13 @@ import { MONO_FONT } from './fonts'
|
|
|
15
15
|
interface Props {
|
|
16
16
|
rows: AchievementCatalogRow[]
|
|
17
17
|
label?: string
|
|
18
|
-
// Independently optional overrides for
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
18
|
+
// Independently optional overrides for the heading's and rows' colors. fg and sectionBg go to each
|
|
19
|
+
// <AchievementRow> (whose own defaults are the well's, see wellSurface.ts). fgMuted goes to the heading and
|
|
20
|
+
// the rows; omitted, the heading (drawn on the screen, not on a well) keeps its own screen-derived default
|
|
21
|
+
// and the rows theirs.
|
|
22
22
|
fg?: string
|
|
23
23
|
fgMuted?: string
|
|
24
|
+
sectionBg?: string
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
// The "ALL ACHIEVEMENTS" heading + one <AchievementRow> per row - extracted verbatim from every
|
|
@@ -42,7 +43,7 @@ interface Props {
|
|
|
42
43
|
// about 1.4:1 and 1.8:1 against the light screen's white, so a bar or check in either barely shows;
|
|
43
44
|
// mixed, they're about 5:1 and 6:1 (bronze goes from about 3:1 to 9:1). The badge keeps the true tier
|
|
44
45
|
// color: its glyph is black, which reads on all three.
|
|
45
|
-
export function AchievementCatalogSection({ rows, label = 'ALL ACHIEVEMENTS', fg, fgMuted: fgMutedOverride }: Props) {
|
|
46
|
+
export function AchievementCatalogSection({ rows, label = 'ALL ACHIEVEMENTS', fg, fgMuted: fgMutedOverride, sectionBg }: Props) {
|
|
46
47
|
const { dark } = useAutoPaperTheme()
|
|
47
48
|
const fgMuted = fgMutedOverride ?? (dark ? 'rgba(255,255,255,0.5)' : 'rgba(0,0,0,0.5)')
|
|
48
49
|
const tierAccent = (tierColor: string) => (dark ? tierColor : getBlendedColor('#000000', tierColor, 0.5))
|
|
@@ -53,7 +54,7 @@ export function AchievementCatalogSection({ rows, label = 'ALL ACHIEVEMENTS', fg
|
|
|
53
54
|
{label}
|
|
54
55
|
</Text>
|
|
55
56
|
{rows.map((row) => (
|
|
56
|
-
<AchievementRow key={row.id} icon={row.icon} title={row.title} description={row.description} badgeColor={row.unlockedAt !== undefined ? row.tierColor : LOCKED_BADGE_COLOR} checkColor={tierAccent(row.tierColor)} unlockedLabel={row.unlockedLabel} progress={row.progress} deviceMarker={row.deviceMarker} fg={fg} fgMuted={fgMutedOverride} />
|
|
57
|
+
<AchievementRow key={row.id} icon={row.icon} title={row.title} description={row.description} badgeColor={row.unlockedAt !== undefined ? row.tierColor : LOCKED_BADGE_COLOR} checkColor={tierAccent(row.tierColor)} unlockedLabel={row.unlockedLabel} progress={row.progress} deviceMarker={row.deviceMarker} fg={fg} fgMuted={fgMutedOverride} sectionBg={sectionBg} />
|
|
57
58
|
))}
|
|
58
59
|
</Fragment>
|
|
59
60
|
)
|
package/src/AchievementRow.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { useAutoPaperTheme } from '@rific/auto-paper'
|
|
2
1
|
import { StyleSheet, View } from 'react-native'
|
|
3
2
|
import { Icon, ProgressBar, Text } from 'react-native-paper'
|
|
4
3
|
|
|
4
|
+
import { useWellColors, WELL_SHADOW } from './wellSurface'
|
|
5
|
+
|
|
5
6
|
// A neutral gray for a locked badge's own background - exported so a caller computing its own
|
|
6
7
|
// badge color (unlocked ? tierColor : LOCKED_BADGE_COLOR, matching LightCycles' own achievements.tsx)
|
|
7
8
|
// doesn't need to invent or duplicate this exact tone.
|
|
@@ -32,10 +33,9 @@ interface Props {
|
|
|
32
33
|
// from a specific profile's own tab - optional since only an app with per-profile stat views has
|
|
33
34
|
// this concept at all.
|
|
34
35
|
deviceMarker?: boolean
|
|
35
|
-
// Independently optional overrides for this row's own text/background colors - each defaults to
|
|
36
|
-
// the
|
|
37
|
-
//
|
|
38
|
-
// with its own app-wide chrome palette passes whichever of the three it needs.
|
|
36
|
+
// Independently optional overrides for this row's own text/background colors - each defaults to the well's
|
|
37
|
+
// (wellSurface.ts: the theme's surface and its onSurface/onSurfaceVariant text), like StatSection/StatRow. A
|
|
38
|
+
// caller with its own app-wide chrome palette passes whichever of the three it needs.
|
|
39
39
|
fg?: string
|
|
40
40
|
fgMuted?: string
|
|
41
41
|
sectionBg?: string
|
|
@@ -44,16 +44,15 @@ interface Props {
|
|
|
44
44
|
// One row per achievement - a tier-colored (or locked-gray) badge, title/description, an optional
|
|
45
45
|
// progress bar while locked, and either an unlocked check-mark+label or a lock icon. Extracted
|
|
46
46
|
// verbatim from LightCycles' own achievements.tsx, where this exact row shape backed its entire
|
|
47
|
-
// "ALL ACHIEVEMENTS" list.
|
|
48
|
-
//
|
|
49
|
-
// own
|
|
50
|
-
// chrome palette. Has no opinion on what an achievement actually IS (id, unlock predicate, catalog)
|
|
47
|
+
// "ALL ACHIEVEMENTS" list. Each row is its own well (wellSurface.ts: the theme's surface on MD3's level-1 shadow);
|
|
48
|
+
// fg/fgMuted/its own background default to the well's colors, each independently overridable via the matching
|
|
49
|
+
// prop for a caller with its own chrome palette. Has no opinion on what an achievement actually IS (id, unlock predicate, catalog)
|
|
51
50
|
// - that stays entirely app-local; this is purely the presentational row.
|
|
52
51
|
export function AchievementRow({ icon, title, description, badgeColor, unlockedLabel, checkColor, progress, deviceMarker, fg: fgOverride, fgMuted: fgMutedOverride, sectionBg: sectionBgOverride }: Props) {
|
|
53
|
-
const
|
|
54
|
-
const fg = fgOverride ??
|
|
55
|
-
const fgMuted = fgMutedOverride ??
|
|
56
|
-
const sectionBg = sectionBgOverride ??
|
|
52
|
+
const well = useWellColors()
|
|
53
|
+
const fg = fgOverride ?? well.fg
|
|
54
|
+
const fgMuted = fgMutedOverride ?? well.fgMuted
|
|
55
|
+
const sectionBg = sectionBgOverride ?? well.bg
|
|
57
56
|
const unlocked = unlockedLabel !== undefined
|
|
58
57
|
|
|
59
58
|
return (
|
|
@@ -116,6 +115,7 @@ const styles = StyleSheet.create({
|
|
|
116
115
|
row: {
|
|
117
116
|
alignItems: 'center',
|
|
118
117
|
borderRadius: 12,
|
|
118
|
+
boxShadow: WELL_SHADOW,
|
|
119
119
|
flexDirection: 'row',
|
|
120
120
|
gap: 12,
|
|
121
121
|
padding: 12
|
|
@@ -9,6 +9,11 @@ interface ActivityStats {
|
|
|
9
9
|
|
|
10
10
|
interface Props {
|
|
11
11
|
stats: ActivityStats
|
|
12
|
+
// Optional overrides for the section's and its rows' colors, passed straight through to StatSection and each
|
|
13
|
+
// StatRow (whose own defaults are the well's, see wellSurface.ts) - for a caller with its own chrome palette.
|
|
14
|
+
fg?: string
|
|
15
|
+
fgMuted?: string
|
|
16
|
+
sectionBg?: string
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
// The "ACTIVITY" StatSection (Days Played / Day Streak / Best Day Streak) - extracted verbatim
|
|
@@ -18,12 +23,12 @@ interface Props {
|
|
|
18
23
|
// currentDayStreak/bestDayStreak) rather than importing DayStreakState itself, so this component
|
|
19
24
|
// has no dependency on @tastic/achievements at all: any caller's own stats/statsView object that
|
|
20
25
|
// happens to carry these three fields already satisfies it structurally.
|
|
21
|
-
export function ActivityStatSection({ stats }: Props) {
|
|
26
|
+
export function ActivityStatSection({ stats, fg, fgMuted, sectionBg }: Props) {
|
|
22
27
|
return (
|
|
23
|
-
<StatSection label='ACTIVITY'>
|
|
24
|
-
<StatRow label='Days Played' value={String(stats.distinctDaysPlayed)} />
|
|
25
|
-
<StatRow label='Day Streak' value={String(stats.currentDayStreak)} />
|
|
26
|
-
<StatRow label='Best Day Streak' value={String(stats.bestDayStreak)} />
|
|
28
|
+
<StatSection label='ACTIVITY' fg={fg} sectionBg={sectionBg}>
|
|
29
|
+
<StatRow label='Days Played' value={String(stats.distinctDaysPlayed)} fg={fg} fgMuted={fgMuted} />
|
|
30
|
+
<StatRow label='Day Streak' value={String(stats.currentDayStreak)} fg={fg} fgMuted={fgMuted} />
|
|
31
|
+
<StatRow label='Best Day Streak' value={String(stats.bestDayStreak)} fg={fg} fgMuted={fgMuted} />
|
|
27
32
|
</StatSection>
|
|
28
33
|
)
|
|
29
34
|
}
|
package/src/StatRow.tsx
CHANGED
|
@@ -1,29 +1,28 @@
|
|
|
1
|
-
import { useAutoPaperTheme } from '@rific/auto-paper'
|
|
2
1
|
import { StyleSheet, View } from 'react-native'
|
|
3
2
|
import { Text } from 'react-native-paper'
|
|
4
3
|
|
|
4
|
+
import { useWellColors } from './wellSurface'
|
|
5
|
+
|
|
5
6
|
interface Props {
|
|
6
7
|
label: string
|
|
7
8
|
value: string
|
|
8
|
-
// Independently optional overrides for this row's own text colors - default to the
|
|
9
|
-
//
|
|
10
|
-
// when omitted, so every existing caller renders identically to before.
|
|
9
|
+
// Independently optional overrides for this row's own text colors - default to the text colors of the well it
|
|
10
|
+
// sits in (wellSurface.ts: onSurface, onSurfaceVariant).
|
|
11
11
|
fg?: string
|
|
12
12
|
fgMuted?: string
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
// A single labeled stat - label on the left (muted), value on the right (bold, full-contrast).
|
|
16
16
|
// Extracted from LightCycles' own achievements.tsx, where this exact row shape backed every
|
|
17
|
-
// section (Overall/Vs CPU/Two Player/Activity). fg/fgMuted default to the
|
|
18
|
-
//
|
|
19
|
-
// each independently overridable via the matching prop for a caller with its own chrome palette.
|
|
17
|
+
// section (Overall/Vs CPU/Two Player/Activity). fg/fgMuted default to the text colors of the StatSection well it
|
|
18
|
+
// sits in, each independently overridable via the matching prop for a caller with its own chrome palette.
|
|
20
19
|
// Deliberately just label+value text, not a color swatch or profile chip - an app needing a row
|
|
21
|
-
// with a leading visual builds its own on top of
|
|
20
|
+
// with a leading visual builds its own on top of the same useWellColors() (see LightCycles' own
|
|
22
21
|
// ColorStatRow/ProfileRankingRow for an example), since what that visual even is varies per game.
|
|
23
22
|
export function StatRow({ label, value, fg: fgOverride, fgMuted: fgMutedOverride }: Props) {
|
|
24
|
-
const
|
|
25
|
-
const fg = fgOverride ??
|
|
26
|
-
const fgMuted = fgMutedOverride ??
|
|
23
|
+
const well = useWellColors()
|
|
24
|
+
const fg = fgOverride ?? well.fg
|
|
25
|
+
const fgMuted = fgMutedOverride ?? well.fgMuted
|
|
27
26
|
|
|
28
27
|
return (
|
|
29
28
|
<View style={styles.row}>
|
package/src/StatSection.tsx
CHANGED
|
@@ -1,29 +1,28 @@
|
|
|
1
|
-
import { useAutoPaperTheme } from '@rific/auto-paper'
|
|
2
1
|
import { ReactNode } from 'react'
|
|
3
2
|
import { StyleSheet, View } from 'react-native'
|
|
4
3
|
import { Text } from 'react-native-paper'
|
|
5
4
|
|
|
5
|
+
import { useWellColors, WELL_SHADOW } from './wellSurface'
|
|
6
|
+
|
|
6
7
|
interface Props {
|
|
7
8
|
label: string
|
|
8
|
-
// Independently optional overrides for this section's own label color/background - default to
|
|
9
|
-
// the
|
|
10
|
-
// own doc for why) when omitted, so every existing caller renders identically to before.
|
|
9
|
+
// Independently optional overrides for this section's own label color/background - default to the well's
|
|
10
|
+
// (wellSurface.ts: the theme's surface and its onSurface text).
|
|
11
11
|
fg?: string
|
|
12
12
|
sectionBg?: string
|
|
13
13
|
children: ReactNode
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
// A labeled
|
|
17
|
-
// shape from LightCycles' own achievements.tsx, generalized to any
|
|
18
|
-
// StatRow). fg/sectionBg default to the
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
// rendering the string it's given.
|
|
16
|
+
// A labeled grouping box, a well (wellSurface.ts: the theme's surface on MD3's level-1 shadow) - the
|
|
17
|
+
// "OVERALL"/"VS CPU"/"ACTIVITY"-style section shape from LightCycles' own achievements.tsx, generalized to any
|
|
18
|
+
// content (typically a stack of StatRow). fg/sectionBg default to the well's colors, each independently
|
|
19
|
+
// overridable via the matching prop for a caller with its own chrome palette. It was a faint tint until
|
|
20
|
+
// 2026-10-17 (6% white in dark mode, 4% black in light). The label itself carries the section's own meaning
|
|
21
|
+
// (what it's a section OF), so this component has no opinion on that beyond rendering the string it's given.
|
|
23
22
|
export function StatSection({ label, fg: fgOverride, sectionBg: sectionBgOverride, children }: Props) {
|
|
24
|
-
const
|
|
25
|
-
const fg = fgOverride ??
|
|
26
|
-
const sectionBg = sectionBgOverride ??
|
|
23
|
+
const well = useWellColors()
|
|
24
|
+
const fg = fgOverride ?? well.fg
|
|
25
|
+
const sectionBg = sectionBgOverride ?? well.bg
|
|
27
26
|
|
|
28
27
|
return (
|
|
29
28
|
<View style={[styles.section, { backgroundColor: sectionBg }]}>
|
|
@@ -46,6 +45,7 @@ const styles = StyleSheet.create({
|
|
|
46
45
|
},
|
|
47
46
|
section: {
|
|
48
47
|
borderRadius: 12,
|
|
48
|
+
boxShadow: WELL_SHADOW,
|
|
49
49
|
gap: 8,
|
|
50
50
|
padding: 16
|
|
51
51
|
}
|
package/src/chrome/index.ts
CHANGED
|
@@ -23,3 +23,4 @@ export { StatRow } from '../StatRow'
|
|
|
23
23
|
export { StatSection } from '../StatSection'
|
|
24
24
|
export { UpdateDialog, type UpdateDialogProps } from '../UpdateDialog'
|
|
25
25
|
export { useCompactDialog } from '../useCompactDialog'
|
|
26
|
+
export { useWellColors, WELL_SHADOW, type WellColors } from '../wellSurface'
|