@mrmeg/expo-ui 0.17.0 → 0.18.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/CHANGELOG.md CHANGED
@@ -3,6 +3,19 @@
3
3
  All notable changes to `@mrmeg/expo-ui` are documented here. This project
4
4
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
5
 
6
+ ## [0.18.0]
7
+
8
+ ### Added
9
+
10
+ - **`BottomSheet.Content` `backgroundStyle` pass-through.** The native sheet
11
+ surface (web vaul panel, Android `containerColor`, iOS
12
+ `presentationBackground`) previously hardcoded the themed card color.
13
+ `BottomSheet.Content` now accepts an optional `backgroundStyle` merged over
14
+ that default, so hosts can pass `{ backgroundColor: "transparent" }` to let
15
+ custom chrome (e.g. a Liquid Glass backdrop) show through. Omitting the prop
16
+ preserves the existing card background. The RN content column still paints
17
+ its own card fill; clear that via `style` when going transparent.
18
+
6
19
  ## [0.17.0]
7
20
 
8
21
  ### Added
package/LLM_USAGE.md CHANGED
@@ -76,6 +76,11 @@ On native, `BottomSheet.Content` avoids the soft keyboard by default with
76
76
  React Native keyboard events. Pass `avoidKeyboard={false}` to opt out for a
77
77
  specific sheet.
78
78
 
79
+ `BottomSheet.Content` themes the native sheet surface with the card color.
80
+ Pass `backgroundStyle={{ backgroundColor: "transparent" }}` (with a `style`
81
+ that clears the content column's card fill) when custom chrome such as a
82
+ glass backdrop should show through; omitting it keeps the card background.
83
+
79
84
  i18n is optional. Do not add app-level i18n setup just to use this package.
80
85
  Plain children and `text` props work without `i18next` or `react-i18next`.
81
86
  `tx` props render fallback text when provided and otherwise render the key
package/README.md CHANGED
@@ -374,7 +374,7 @@ Use `StyledText` or its aliases instead of raw `Text` whenever the text is part
374
374
  />
375
375
  ```
376
376
 
377
- Mount `UIProvider` once near the root before using `Dialog`, `AlertDialog`, `BottomSheet`, `Drawer`, `DropdownMenu`, `Popover`, `SelectContent`, `Tooltip`, or package notifications. On native, `UIProvider` also wraps app content in the package keyboard-avoiding root by default, so ordinary screens and fixed footers stay above the soft keyboard without repeated app-local wrappers; pass `keyboardAvoiding={false}` to opt out, or use `KeyboardAvoidingView` directly for a subtree with custom behavior. Web skips the root keyboard wrapper unless `keyboardAvoiding` is explicitly enabled. `BottomSheet.Content` listens to React Native keyboard events when `avoidKeyboard` is enabled; it defaults to `true` and can be disabled per sheet. Trigger transient feedback with `notify`.
377
+ Mount `UIProvider` once near the root before using `Dialog`, `AlertDialog`, `BottomSheet`, `Drawer`, `DropdownMenu`, `Popover`, `SelectContent`, `Tooltip`, or package notifications. On native, `UIProvider` also wraps app content in the package keyboard-avoiding root by default, so ordinary screens and fixed footers stay above the soft keyboard without repeated app-local wrappers; pass `keyboardAvoiding={false}` to opt out, or use `KeyboardAvoidingView` directly for a subtree with custom behavior. Web skips the root keyboard wrapper unless `keyboardAvoiding` is explicitly enabled. `BottomSheet.Content` listens to React Native keyboard events when `avoidKeyboard` is enabled; it defaults to `true` and can be disabled per sheet. `BottomSheet.Content` also accepts `backgroundStyle`, merged over the themed card default on the native sheet surface (web panel, Android `containerColor`, iOS `presentationBackground`) — pass `{ backgroundColor: "transparent" }`, plus a `style` clearing the content column's card fill, to let custom chrome like a glass backdrop show through. Trigger transient feedback with `notify`.
378
378
 
379
379
  Use `Skeleton` components for loading content with stable dimensions, `EmptyState` for no-data/recoverable errors, `Alert` for blocking confirm/alert dialogs, and `Notification` for transient global feedback.
380
380
 
@@ -117,6 +117,14 @@ interface BottomSheetContentProps extends ViewProps {
117
117
  avoidKeyboard?: boolean;
118
118
  /** Accepted for call-site ergonomics; ignored (platform owns keyboard). */
119
119
  dismissKeyboardOnDrag?: boolean;
120
+ /**
121
+ * Style for the native sheet surface behind the RN content — the web (vaul)
122
+ * panel, Android `containerColor`, iOS `presentationBackground`. Merged over
123
+ * the themed card default, so `{ backgroundColor: "transparent" }` lets a
124
+ * custom chrome (e.g. a glass backdrop) show through. The RN content column
125
+ * paints its own card fill too; clear that via `style`.
126
+ */
127
+ backgroundStyle?: StyleProp<ViewStyle>;
120
128
  style?: StyleProp<ViewStyle>;
121
129
  children: React.ReactNode;
122
130
  }
@@ -140,7 +148,7 @@ interface BottomSheetCloseProps {
140
148
  declare function useBottomSheetContext(): BottomSheetContextValue;
141
149
  declare function BottomSheetRoot({ open: controlledOpen, onOpenChange: controlledOnOpenChange, defaultOpen, snapPoints, closeOnBackdropPress, children, }: BottomSheetProps): React.JSX.Element;
142
150
  declare function BottomSheetTrigger({ asChild, children, style: styleOverride }: BottomSheetTriggerProps): React.JSX.Element;
143
- declare function BottomSheetContent({ swipeEnabled: _swipeEnabled, avoidKeyboard: _avoidKeyboard, dismissKeyboardOnDrag: _dismissKeyboardOnDrag, style: styleOverride, children, }: BottomSheetContentProps): React.JSX.Element;
151
+ declare function BottomSheetContent({ swipeEnabled: _swipeEnabled, avoidKeyboard: _avoidKeyboard, dismissKeyboardOnDrag: _dismissKeyboardOnDrag, backgroundStyle: backgroundStyleOverride, style: styleOverride, children, }: BottomSheetContentProps): React.JSX.Element;
144
152
  declare function BottomSheetHandle({ style }: BottomSheetHandleProps): React.JSX.Element;
145
153
  declare function BottomSheetHeader({ children, style, ...props }: BottomSheetHeaderProps): React.JSX.Element;
146
154
  declare function BottomSheetBody({ children, style, contentContainerStyle, onContentSizeChange, onLayout, ...props }: BottomSheetBodyProps): React.JSX.Element;
@@ -296,7 +296,7 @@ function BottomSheetTrigger({ asChild, children, style: styleOverride }) {
296
296
  // ============================================================================
297
297
  function BottomSheetContent({
298
298
  // Accepted-but-ignored ergonomics props (platform owns these behaviors):
299
- swipeEnabled: _swipeEnabled, avoidKeyboard: _avoidKeyboard, dismissKeyboardOnDrag: _dismissKeyboardOnDrag, style: styleOverride, children, }) {
299
+ swipeEnabled: _swipeEnabled, avoidKeyboard: _avoidKeyboard, dismissKeyboardOnDrag: _dismissKeyboardOnDrag, backgroundStyle: backgroundStyleOverride, style: styleOverride, children, }) {
300
300
  const { open, onOpenChange, snapPoints, snapIndex, setSnapIndex, hasHeader } = useBottomSheetContext();
301
301
  const { theme } = useTheme();
302
302
  const dismissDisabled = useDismissDisabled();
@@ -337,8 +337,13 @@ swipeEnabled: _swipeEnabled, avoidKeyboard: _avoidKeyboard, dismissKeyboardOnDra
337
337
  if (open)
338
338
  onOpenChange(false);
339
339
  },
340
- // Themes the scrim/background on web (vaul) and Android (containerColor).
341
- backgroundStyle: { backgroundColor: theme.colors.card }, children: _jsx(TextColorContext.Provider, { value: theme.colors.foreground, children: _jsx(TextClassContext.Provider, { value: "", children: _jsxs(View, { style: [
340
+ // Themes the scrim/background on web (vaul), Android (containerColor),
341
+ // and iOS (presentationBackground). Flattened so native readers that
342
+ // expect a plain object (not a style array) keep working.
343
+ backgroundStyle: StyleSheet.flatten([
344
+ { backgroundColor: theme.colors.card },
345
+ backgroundStyleOverride,
346
+ ]), children: _jsx(TextColorContext.Provider, { value: theme.colors.foreground, children: _jsx(TextClassContext.Provider, { value: "", children: _jsxs(View, { style: [
342
347
  {
343
348
  flex: 1,
344
349
  maxHeight: detentHeight,
@@ -1,3 +1,17 @@
1
+ /**
2
+ * Registers the Feather icon font in expo-font's per-request SSR store.
3
+ *
4
+ * Must be called from a **render body** on server-web (see `useResources`
5
+ * below): expo-font enters an `AsyncLocalStorage` store per request, so a
6
+ * module-scope registration lands in at most one request's store. Registering
7
+ * during render means every request emits the `@font-face` rule into
8
+ * `<style id="expo-generated-fonts">` and server-rendered `<Icon>`s agree with
9
+ * the client's first render.
10
+ *
11
+ * No-op on native and on client web (both already covered: native by the
12
+ * effect below, client web by the module-scope call above).
13
+ */
14
+ export declare function ensureIconFontRegistered(): void;
1
15
  interface LoadResourcesResult {
2
16
  loaded: boolean;
3
17
  error: Error | null;
@@ -4,15 +4,45 @@ import Feather from "@expo/vector-icons/Feather";
4
4
  import { Inter_400Regular, Inter_500Medium, Inter_600SemiBold, Inter_700Bold, } from "@expo-google-fonts/inter";
5
5
  import { Platform } from "react-native";
6
6
  import { useThemeStore } from "../state/themeStore.js";
7
- // Eager, module-scope load so expo-font registers Feather in its SSR
8
- // serverContext. When the server renders the HTML document, expo-font emits
9
- // an @font-face <style> into the head — without this kickoff, server-rendered
10
- // <Icon> components paint as empty glyphs until hydration runs the effect
11
- // below, producing a visible icon-pop flash on icon-heavy SSR screens
12
- // (e.g. OnboardingFlow). On the client this also primes the font ahead of
13
- // the effect; the effect's loadAsync becomes a no-op for the already-loaded
14
- // font.
15
- void Font.loadAsync(Feather.font);
7
+ // Eager, module-scope load that primes the Feather font on the *client* before
8
+ // first render: on web it synchronously injects the @font-face rule, which is
9
+ // what makes the client's `Font.isLoaded("feather")` check read true during
10
+ // hydration; on native it just starts the fetch ahead of the effect below
11
+ // (which then no-ops for the already-loaded font).
12
+ //
13
+ // Deliberately guarded off server-web. expo-font's SSR store is per-request
14
+ // AsyncLocalStorage, and Metro's inlineRequires defers this module's evaluation
15
+ // until it is first required during a render — so on the server this line only
16
+ // ever populated the store of whichever request happened to warm the module
17
+ // cache. Requests 2+ then shipped HTML with no @font-face and empty icons while
18
+ // the client rendered glyphs (React #418). Server registration happens
19
+ // per-request in ensureIconFontRegistered() instead.
20
+ if (typeof window !== "undefined") {
21
+ void Font.loadAsync(Feather.font);
22
+ }
23
+ /**
24
+ * Registers the Feather icon font in expo-font's per-request SSR store.
25
+ *
26
+ * Must be called from a **render body** on server-web (see `useResources`
27
+ * below): expo-font enters an `AsyncLocalStorage` store per request, so a
28
+ * module-scope registration lands in at most one request's store. Registering
29
+ * during render means every request emits the `@font-face` rule into
30
+ * `<style id="expo-generated-fonts">` and server-rendered `<Icon>`s agree with
31
+ * the client's first render.
32
+ *
33
+ * No-op on native and on client web (both already covered: native by the
34
+ * effect below, client web by the module-scope call above).
35
+ */
36
+ export function ensureIconFontRegistered() {
37
+ if (Platform.OS !== "web" || typeof window !== "undefined")
38
+ return;
39
+ if (Font.isLoaded("feather"))
40
+ return; // per-request store; cheap
41
+ // Synchronous on server-web (expo-font routes to registerStaticFont). No
42
+ // `void`/try-catch: a throw here means we're outside a request scope, which
43
+ // must be loud.
44
+ Font.loadAsync(Feather.font);
45
+ }
16
46
  // The four static Inter weights StyledText's native family keys point at
17
47
  // (see constants/fonts.ts). Native-only: web never renders these family names
18
48
  // ("Inter_400Regular" etc.) — fontFamilies.sansSerif resolves every weight to
@@ -66,6 +96,10 @@ function ensureWebFontStylesheet() {
66
96
  * un-download Inter. The Feather icon font always loads.
67
97
  */
68
98
  export const useResources = () => {
99
+ // Render-body (not effect) so server-web registers the icon font in *this*
100
+ // request's expo-font store — effects never run on the server and a
101
+ // module-scope call only reaches one request. See ensureIconFontRegistered.
102
+ ensureIconFontRegistered();
69
103
  const [loaded, setLoaded] = useState(false);
70
104
  const [error, setError] = useState(null);
71
105
  useEffect(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrmeg/expo-ui",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "private": false,
5
5
  "description": "Reusable Expo and React Native UI primitives for MrMeg projects.",
6
6
  "keywords": [