@tastic/shell 0.3.0 → 0.3.1
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 +4 -2
- package/dist/index.js +12 -7
- package/dist/index.mjs +13 -8
- package/package.json +1 -1
- package/src/RootLayout.tsx +31 -19
package/README.md
CHANGED
|
@@ -18,7 +18,9 @@ package ecosystem, on [`@tastic/hud`](https://github.com/jayrdeaton/react-native
|
|
|
18
18
|
|
|
19
19
|
- **`RootLayout`**: the root of the app (gesture root, safe area, fonts, Redux and its persist gate,
|
|
20
20
|
the theme, profiles, every game's own provider, the splash gates, the shared orientation reading,
|
|
21
|
-
toasts, How to Play, the OTA update dialog) around expo-router's stack.
|
|
21
|
+
toasts, How to Play, the OTA update dialog) around expo-router's stack. The stack follows the app's
|
|
22
|
+
own theme, not the phone's light or dark appearance: the status bar's text, the root window and the
|
|
23
|
+
navigation theme's background (what shows in a sliding screen's corners during a push or pop).
|
|
22
24
|
- **Screens** for the routes: `HomeScreen` (the pack's hero, seat 1's button, a row per game, or a
|
|
23
25
|
grid of preview tiles on a wide screen), `LoadoutRoute` and `PlayRoute` (`loadout/[game]`,
|
|
24
26
|
`play/[game]`), `ProfilesRoute` and `StatsScreen`.
|
|
@@ -112,7 +114,7 @@ PuckTastic and DuelTastic draw what they always did:
|
|
|
112
114
|
| `Backdrop?` | A table drawn once behind the whole stack (Solitaire's felt). The stack's two layers go transparent (the screens' `contentStyle` and the navigation theme's `colors.background`, which React Navigation paints around every screen), the root window stays dark so light mode doesn't flash white on a push, Android's push and pop crossfade (its default holds the outgoing screen opaque), and Home, each loadout, the stats screen and Profiles draw transparent, with their buttons and tabs on the theme's surface. |
|
|
113
115
|
| `foreground?` | The color of what those screens draw straight on the background (the hero, the corner buttons, headers), and at half strength a loadout's muted options. Unset is black or white with the theme. |
|
|
114
116
|
| `Provider?` | Mounted once around How to Play and the stack, inside the store, the theme, profiles and every game's `Provider` (one stats context across the pack's games). |
|
|
115
|
-
| `useScreenOptions?` | A hook returning stack screen options (`StackScreenOptions`), merged after the shell's own (a
|
|
117
|
+
| `useScreenOptions?` | A hook returning stack screen options (`StackScreenOptions`), merged after the shell's own (a status bar style of the pack's own, Reduce Motion's crossfade). The shell's already follow the app's theme, and stay dark over a `Backdrop`. |
|
|
116
118
|
| `useFeedbackSounds?` | A hook returning the two sounds every button plays (`FeedbackSounds`: `selection`, `notification`), in place of the shell's two wavs. |
|
|
117
119
|
| `Toaster?` | The pack's own toaster, drawn in a frame turned with the phone (with the safe-area insets turned onto its edges) in place of the shell's. The toast provider is given no Paper, so nothing portals it out of the frame. |
|
|
118
120
|
| `portraitWhileKeyboard?` | Every screen reads upright while the software keyboard is up (`@tastic/core`'s `OrientationProvider`), for naming a profile sideways. |
|
package/dist/index.js
CHANGED
|
@@ -484,8 +484,9 @@ function AppStack() {
|
|
|
484
484
|
const pack = usePack();
|
|
485
485
|
const { Backdrop } = pack;
|
|
486
486
|
const { dark } = (0, import_auto_paper2.useAutoPaperTheme)();
|
|
487
|
-
const
|
|
488
|
-
const
|
|
487
|
+
const darkRoot = Backdrop ? true : dark;
|
|
488
|
+
const rootBackground = (0, import_core6.useThemedRootBackground)(darkRoot, false);
|
|
489
|
+
const statusBar = (0, import_core6.useRotationAwareStatusBar)({ style: darkRoot ? "light" : "dark" });
|
|
489
490
|
const packOptions = (pack.useScreenOptions ?? noScreenOptions)();
|
|
490
491
|
const backdropOptions = Backdrop ? { contentStyle: TRANSPARENT_CONTENT, animation: import_react_native2.Platform.select({ android: "fade" }) } : void 0;
|
|
491
492
|
const playOptions = (0, import_react9.useCallback)(
|
|
@@ -495,15 +496,19 @@ function AppStack() {
|
|
|
495
496
|
},
|
|
496
497
|
[pack.games]
|
|
497
498
|
);
|
|
498
|
-
const
|
|
499
|
-
const
|
|
499
|
+
const navigationBackground = Backdrop ? TRANSPARENT_CONTENT.backgroundColor : rootBackground.contentStyle.backgroundColor;
|
|
500
|
+
const navigationTheme = (0, import_react9.useMemo)(() => {
|
|
501
|
+
const base = darkRoot ? import_expo_router.DarkTheme : import_expo_router.DefaultTheme;
|
|
502
|
+
return { ...base, colors: { ...base.colors, background: navigationBackground } };
|
|
503
|
+
}, [darkRoot, navigationBackground]);
|
|
504
|
+
const stack = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_expo_router.ThemeProvider, { value: navigationTheme, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_expo_router.Stack, { screenOptions: { ...rootBackground, ...statusBar, ...backdropOptions, ...packOptions }, children: [
|
|
500
505
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_expo_router.Stack.Screen, { name: "index" }),
|
|
501
506
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_expo_router.Stack.Screen, { name: "play/[game]", options: playOptions })
|
|
502
|
-
] });
|
|
503
|
-
if (!Backdrop
|
|
507
|
+
] }) });
|
|
508
|
+
if (!Backdrop) return stack;
|
|
504
509
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native2.View, { style: styles.root, children: [
|
|
505
510
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Backdrop, {}),
|
|
506
|
-
|
|
511
|
+
stack
|
|
507
512
|
] });
|
|
508
513
|
}
|
|
509
514
|
function OrientationBridge({ children }) {
|
package/dist/index.mjs
CHANGED
|
@@ -29,7 +29,7 @@ import { OrientationProvider, useRotationAwareStatusBar, useThemedRootBackground
|
|
|
29
29
|
import { RotatedFrame } from "@tastic/hud/chrome";
|
|
30
30
|
import { ProfileColorProvider } from "@tastic/profile";
|
|
31
31
|
import * as Haptics from "expo-haptics";
|
|
32
|
-
import { DarkTheme, Stack, ThemeProvider as NavigationThemeProvider } from "expo-router";
|
|
32
|
+
import { DarkTheme, DefaultTheme, Stack, ThemeProvider as NavigationThemeProvider } from "expo-router";
|
|
33
33
|
import { DeviceMotion } from "expo-sensors";
|
|
34
34
|
import { useCallback as useCallback6, useMemo as useMemo3 } from "react";
|
|
35
35
|
import { Platform as Platform2, StyleSheet, View } from "react-native";
|
|
@@ -410,8 +410,9 @@ function AppStack() {
|
|
|
410
410
|
const pack = usePack();
|
|
411
411
|
const { Backdrop } = pack;
|
|
412
412
|
const { dark } = useAutoPaperTheme();
|
|
413
|
-
const
|
|
414
|
-
const
|
|
413
|
+
const darkRoot = Backdrop ? true : dark;
|
|
414
|
+
const rootBackground = useThemedRootBackground(darkRoot, false);
|
|
415
|
+
const statusBar = useRotationAwareStatusBar({ style: darkRoot ? "light" : "dark" });
|
|
415
416
|
const packOptions = (pack.useScreenOptions ?? noScreenOptions)();
|
|
416
417
|
const backdropOptions = Backdrop ? { contentStyle: TRANSPARENT_CONTENT, animation: Platform2.select({ android: "fade" }) } : void 0;
|
|
417
418
|
const playOptions = useCallback6(
|
|
@@ -421,15 +422,19 @@ function AppStack() {
|
|
|
421
422
|
},
|
|
422
423
|
[pack.games]
|
|
423
424
|
);
|
|
424
|
-
const
|
|
425
|
-
const
|
|
425
|
+
const navigationBackground = Backdrop ? TRANSPARENT_CONTENT.backgroundColor : rootBackground.contentStyle.backgroundColor;
|
|
426
|
+
const navigationTheme = useMemo3(() => {
|
|
427
|
+
const base = darkRoot ? DarkTheme : DefaultTheme;
|
|
428
|
+
return { ...base, colors: { ...base.colors, background: navigationBackground } };
|
|
429
|
+
}, [darkRoot, navigationBackground]);
|
|
430
|
+
const stack = /* @__PURE__ */ jsx9(NavigationThemeProvider, { value: navigationTheme, children: /* @__PURE__ */ jsxs2(Stack, { screenOptions: { ...rootBackground, ...statusBar, ...backdropOptions, ...packOptions }, children: [
|
|
426
431
|
/* @__PURE__ */ jsx9(Stack.Screen, { name: "index" }),
|
|
427
432
|
/* @__PURE__ */ jsx9(Stack.Screen, { name: "play/[game]", options: playOptions })
|
|
428
|
-
] });
|
|
429
|
-
if (!Backdrop
|
|
433
|
+
] }) });
|
|
434
|
+
if (!Backdrop) return stack;
|
|
430
435
|
return /* @__PURE__ */ jsxs2(View, { style: styles.root, children: [
|
|
431
436
|
/* @__PURE__ */ jsx9(Backdrop, {}),
|
|
432
|
-
|
|
437
|
+
stack
|
|
433
438
|
] });
|
|
434
439
|
}
|
|
435
440
|
function OrientationBridge({ children }) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tastic/shell",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "The app around a pack of React Native games, two-seat or for one player: Home, the loadout, the stats screen, Profiles and Settings, the pack's Redux store and its root providers, driven by one PackDefinition",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
package/src/RootLayout.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import { OrientationProvider, useRotationAwareStatusBar, useThemedRootBackground
|
|
|
5
5
|
import { RotatedFrame } from '@tastic/hud/chrome'
|
|
6
6
|
import { ProfileColorProvider } from '@tastic/profile'
|
|
7
7
|
import * as Haptics from 'expo-haptics'
|
|
8
|
-
import { DarkTheme, Stack, ThemeProvider as NavigationThemeProvider } from 'expo-router'
|
|
8
|
+
import { DarkTheme, DefaultTheme, Stack, ThemeProvider as NavigationThemeProvider } from 'expo-router'
|
|
9
9
|
import { DeviceMotion } from 'expo-sensors'
|
|
10
10
|
import { ReactNode, useCallback, useMemo } from 'react'
|
|
11
11
|
import { Platform, StyleSheet, View } from 'react-native'
|
|
@@ -50,13 +50,16 @@ import type { PackDefinition, PackGameDefinition } from './types'
|
|
|
50
50
|
const PLAY_OPTIONS = { statusBarHidden: true }
|
|
51
51
|
const KEEP_STATUS_BAR_OPTIONS = {}
|
|
52
52
|
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
53
|
+
// The navigation theme is the root background too. React Navigation paints its theme's
|
|
54
|
+
// `colors.background` on the layers around every screen (the ScreenStack's container on native, each
|
|
55
|
+
// screen's wrapper on the web), and expo-router hands its container no theme, so that was always
|
|
56
|
+
// DefaultTheme's light grey: on iOS it showed in a sliding screen's rounded corners during every push and
|
|
57
|
+
// pop, whatever the app's theme. So the stack always gets a theme whose background is the root
|
|
58
|
+
// background's own color, and a pack's Backdrop (PackDefinition.Backdrop), drawn once behind the whole
|
|
59
|
+
// stack, gets a transparent one so it shows through both layers (contentStyle alone left an opaque light
|
|
60
|
+
// grey slab over Solitaire's felt on the web). Nothing else reads the navigation theme (headers are
|
|
61
|
+
// hidden; Paper has its own). Built as the stack renders, never at import, where a pack's tests may stand
|
|
62
|
+
// in for expo-router without its themes.
|
|
60
63
|
const TRANSPARENT_CONTENT = { backgroundColor: 'transparent' }
|
|
61
64
|
|
|
62
65
|
// The pack's own screen options (PackDefinition.useScreenOptions), as a hook every pack calls: an
|
|
@@ -67,10 +70,13 @@ function AppStack() {
|
|
|
67
70
|
const pack = usePack()
|
|
68
71
|
const { Backdrop } = pack
|
|
69
72
|
const { dark } = useAutoPaperTheme()
|
|
70
|
-
// Over a backdrop the root
|
|
71
|
-
//
|
|
72
|
-
const
|
|
73
|
-
const
|
|
73
|
+
// Over a backdrop the root stays dark whatever the mode: a backdrop is a dark table (the felt), and a
|
|
74
|
+
// light root window would flash white around it during a push or pop.
|
|
75
|
+
const darkRoot = Backdrop ? true : dark
|
|
76
|
+
const rootBackground = useThemedRootBackground(darkRoot, false)
|
|
77
|
+
// The status bar's text follows the app's theme, not the phone's appearance (react-native-screens'
|
|
78
|
+
// default `auto`), which drew dark text on a dark theme's black Home with the phone set to light.
|
|
79
|
+
const statusBar = useRotationAwareStatusBar({ style: darkRoot ? 'light' : 'dark' })
|
|
74
80
|
const packOptions = (pack.useScreenOptions ?? noScreenOptions)()
|
|
75
81
|
// Over a backdrop every screen is transparent, and Android crossfades: its default push and pop hold
|
|
76
82
|
// the outgoing screen opaque for ~450ms while the new one slides over it, which would show Home
|
|
@@ -83,18 +89,24 @@ function AppStack() {
|
|
|
83
89
|
},
|
|
84
90
|
[pack.games]
|
|
85
91
|
)
|
|
86
|
-
const
|
|
92
|
+
const navigationBackground = Backdrop ? TRANSPARENT_CONTENT.backgroundColor : rootBackground.contentStyle.backgroundColor
|
|
93
|
+
const navigationTheme = useMemo(() => {
|
|
94
|
+
const base = darkRoot ? DarkTheme : DefaultTheme
|
|
95
|
+
return { ...base, colors: { ...base.colors, background: navigationBackground } }
|
|
96
|
+
}, [darkRoot, navigationBackground])
|
|
87
97
|
const stack = (
|
|
88
|
-
<
|
|
89
|
-
<Stack
|
|
90
|
-
|
|
91
|
-
|
|
98
|
+
<NavigationThemeProvider value={navigationTheme}>
|
|
99
|
+
<Stack screenOptions={{ ...rootBackground, ...statusBar, ...backdropOptions, ...packOptions }}>
|
|
100
|
+
<Stack.Screen name='index' />
|
|
101
|
+
<Stack.Screen name='play/[game]' options={playOptions} />
|
|
102
|
+
</Stack>
|
|
103
|
+
</NavigationThemeProvider>
|
|
92
104
|
)
|
|
93
|
-
if (!Backdrop
|
|
105
|
+
if (!Backdrop) return stack
|
|
94
106
|
return (
|
|
95
107
|
<View style={styles.root}>
|
|
96
108
|
<Backdrop />
|
|
97
|
-
|
|
109
|
+
{stack}
|
|
98
110
|
</View>
|
|
99
111
|
)
|
|
100
112
|
}
|