@mrmeg/expo-ui 0.1.3 → 0.1.4

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/LLM_USAGE.md CHANGED
@@ -16,7 +16,7 @@ import { Button as ButtonDirect } from "@mrmeg/expo-ui/components/Button";
16
16
  import { colors, spacing, typography } from "@mrmeg/expo-ui/constants";
17
17
  import { useResources, useTheme } from "@mrmeg/expo-ui/hooks";
18
18
  import { globalUIStore, useThemeStore } from "@mrmeg/expo-ui/state";
19
- import { hapticLight } from "@mrmeg/expo-ui/lib";
19
+ import { configureExpoUiI18n, hapticLight } from "@mrmeg/expo-ui/lib";
20
20
  ```
21
21
 
22
22
  The root barrel also exports the public surface:
@@ -68,6 +68,18 @@ export function RootLayout() {
68
68
  `AlertDialog`, `BottomSheet`, `Drawer`, `DropdownMenu`, `Popover`,
69
69
  `SelectContent`, or `Tooltip`.
70
70
 
71
+ i18n is optional. Do not add app-level i18n setup just to use this package.
72
+ Plain children and `text` props work without `i18next` or `react-i18next`.
73
+ `tx` props render the key until the consumer opts in with a package-local
74
+ translator:
75
+
76
+ ```tsx
77
+ import { configureExpoUiI18n } from "@mrmeg/expo-ui/lib";
78
+ import { i18n } from "./i18n";
79
+
80
+ configureExpoUiI18n((key, options) => i18n.t(key, options));
81
+ ```
82
+
71
83
  ## Theme And Text Rules
72
84
 
73
85
  - Use `useTheme()` and semantic tokens instead of hardcoded colors.
package/README.md CHANGED
@@ -28,11 +28,11 @@ Consumers must also install the peer dependencies listed in `package.json`.
28
28
  The tested baseline is Expo SDK 55 with React 19.2, React Native 0.83,
29
29
  React Native Web 0.21, Reanimated 4.2, Worklets 0.7, and
30
30
  `@rn-primitives/*` 1.4. `@rn-primitives/portal` is package-managed because
31
- `UIProvider` mounts the portal host used by package overlays. `i18next` and
32
- `react-i18next` are runtime peers because `StyledText` and `Notification`
33
- support translated text keys. Start consumer apps from the same Expo SDK
34
- family or update the package and peer ranges deliberately. Keep npm auth tokens
35
- in developer or CI configuration, not in this repository.
31
+ `UIProvider` mounts the portal host used by package overlays. i18n setup is
32
+ optional; plain text and children render without `i18next` or
33
+ `react-i18next`. Start consumer apps from the same Expo SDK family or update
34
+ the package and peer ranges deliberately. Keep npm auth tokens in developer or
35
+ CI configuration, not in this repository.
36
36
 
37
37
  ## Imports
38
38
 
@@ -44,7 +44,7 @@ import { colors as colorsDirect } from "@mrmeg/expo-ui/constants/colors";
44
44
  import { useResources, useTheme } from "@mrmeg/expo-ui/hooks";
45
45
  import { useTheme as useThemeDirect } from "@mrmeg/expo-ui/hooks/useTheme";
46
46
  import { globalUIStore, useThemeStore } from "@mrmeg/expo-ui/state";
47
- import { hapticLight } from "@mrmeg/expo-ui/lib";
47
+ import { configureExpoUiI18n, hapticLight } from "@mrmeg/expo-ui/lib";
48
48
  ```
49
49
 
50
50
  The root barrel also exports the public surface:
@@ -116,6 +116,17 @@ Useful `StyledText` props:
116
116
  - `variant`: `sansSerif`, `serif`
117
117
  - `align`, `tx`, `txOptions`
118
118
 
119
+ `tx` support is opt-in. Without a configured translator, `tx` renders the key
120
+ and `text` renders as provided. Consumers that already use i18n can connect it
121
+ once near app startup:
122
+
123
+ ```tsx
124
+ import { configureExpoUiI18n } from "@mrmeg/expo-ui/lib";
125
+ import { i18n } from "./i18n";
126
+
127
+ configureExpoUiI18n((key, options) => i18n.t(key, options));
128
+ ```
129
+
119
130
  ## Component Guide
120
131
 
121
132
  All components are exported from `@mrmeg/expo-ui/components`; direct imports such as `@mrmeg/expo-ui/components/Button` are supported. Use this table as the first stop before building a new primitive in a consumer app.
@@ -336,13 +347,14 @@ publishing instead:
336
347
  1. In npm package settings for `@mrmeg/expo-ui`, add a trusted publisher:
337
348
  GitHub Actions, owner `mrmeg`, repository `expo-template`, workflow
338
349
  filename `publish-ui.yml`.
339
- 2. In GitHub Actions, run the `Publish UI Package` workflow with `version=patch`
340
- and `ref=dev`.
350
+ 2. Bump `packages/ui/package.json` in a commit and push it to `main`.
341
351
 
342
- The workflow uses npm OIDC, not a checked-in token or local npm login. It bumps
343
- the package version, updates `bun.lock`, runs the package gates, lets npm CLI
344
- use the GitHub Actions OIDC environment, commits the version bump, and publishes
345
- from `packages/ui`.
352
+ The workflow uses npm OIDC, not a checked-in token or local npm login. On push,
353
+ it reads the committed package version, skips cleanly if that version is already
354
+ published, otherwise runs the package gates and publishes from `packages/ui`.
355
+ The workflow can also be run manually with `version=patch` and `ref=main`; manual
356
+ runs bump the package version, update `bun.lock`, run the package gates, commit
357
+ the version bump, and publish.
346
358
 
347
359
  Keep `repository.url` in `package.json` as
348
360
  `git+https://github.com/mrmeg/expo-template.git`. npm trusted publishing checks
@@ -353,9 +365,9 @@ If npm trusted publishing is blocked by package settings, add an npm automation
353
365
  or granular publish token to GitHub Actions secrets as `NPM_TOKEN` and rerun the
354
366
  same workflow. The token is used only for the publish step.
355
367
 
356
- If the workflow fails after the version is already bumped, rerun it with the
357
- exact current package version, for example `version=0.1.2`. Exact-version reruns
358
- do not bump again.
368
+ If the manual workflow fails after the version is already bumped, rerun it with
369
+ the exact current package version, for example `version=0.1.3`. Exact-version
370
+ reruns do not bump again.
359
371
 
360
372
  Manual package checks:
361
373
 
@@ -147,13 +147,13 @@ export function Button(props) {
147
147
  },
148
148
  // Spread array styles from Slot to prevent nested arrays on web
149
149
  ...(Array.isArray(styleOverride) ? styleOverride : [styleOverride]),
150
- ], children: [!!LeftAccessory && !loading && (_jsx(LeftAccessory, { style: styles.leftAccessory, pressableState: state, disabled: isDisabled })), loading && (_jsx(ActivityIndicator, { size: "small", color: textColor, style: styles.loader })), (tx || text) ? (_jsx(StyledText, { style: [
150
+ ], children: [!!LeftAccessory && !loading && (_jsx(LeftAccessory, { style: styles.leftAccessory, pressableState: state, disabled: isDisabled })), loading && (_jsx(ActivityIndicator, { size: "small", color: textColor, style: styles.loader })), (tx || text) ? (_jsx(StyledText, { tx: tx, text: text, txOptions: txOptions, style: [
151
151
  styles.text,
152
152
  state.pressed && styles.pressedText,
153
153
  state.pressed && pressedTextStyleOverride,
154
154
  isDisabled && disabledTextStyleOverride,
155
155
  textStyleOverride,
156
- ], children: tx || text })) : !loading && children ? (
156
+ ] })) : !loading && children ? (
157
157
  // Wrap string children in StyledText to apply TextColorContext
158
158
  typeof children === "string" ? (_jsx(StyledText, { style: [
159
159
  styles.text,
@@ -2,13 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useContext, useEffect, useRef } from "react";
3
3
  import { StyleSheet, View, ActivityIndicator, Pressable, Platform } from "react-native";
4
4
  import Animated, { useSharedValue, useAnimatedStyle, withTiming, runOnJS, useReducedMotion, Easing, } from "react-native-reanimated";
5
- import { useTranslation } from "react-i18next";
6
5
  import { SafeAreaInsetsContext } from "react-native-safe-area-context";
7
6
  import { fontFamilies } from "../constants/fonts.js";
8
7
  import { Icon } from "./Icon.js";
9
8
  import { useTheme } from "../hooks/useTheme.js";
10
9
  import { spacing } from "../constants/spacing.js";
11
10
  import { StyledText } from "./StyledText.js";
11
+ import { translateText } from "../lib/i18n.js";
12
12
  import { globalUIStore } from "../state/globalUIStore.js";
13
13
  /**
14
14
  * Notification
@@ -36,7 +36,6 @@ import { globalUIStore } from "../state/globalUIStore.js";
36
36
  * ```
37
37
  */
38
38
  export const Notification = () => {
39
- const { t } = useTranslation();
40
39
  const { theme, getShadowStyle } = useTheme();
41
40
  const reduceMotion = useReducedMotion();
42
41
  const insets = useContext(SafeAreaInsetsContext);
@@ -140,11 +139,11 @@ export const Notification = () => {
140
139
  return alert.title;
141
140
  switch (alert?.type) {
142
141
  case "error":
143
- return t("notification.error");
142
+ return translateText("notification.error");
144
143
  case "success":
145
- return t("notification.success");
144
+ return translateText("notification.success");
146
145
  case "warning":
147
- return t("notification.warning");
146
+ return translateText("notification.warning");
148
147
  case "info":
149
148
  return "";
150
149
  default:
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import React, { forwardRef } from "react";
3
3
  import { Text as RNText, StyleSheet } from "react-native";
4
- import { useTranslation } from "react-i18next";
5
4
  import { useTheme } from "../hooks/useTheme.js";
6
5
  import { fontFamilies } from "../constants/fonts.js";
6
+ import { translateText } from "../lib/i18n.js";
7
7
  /**
8
8
  * TextClassContext provides className context for nested text components
9
9
  * Used by @rn-primitives to apply consistent styling through the component tree
@@ -75,7 +75,6 @@ const getFontFamilyWeight = (weight) => {
75
75
  export const StyledText = forwardRef((props, ref) => {
76
76
  const { tx, text, txOptions, style, variant = "sansSerif", fontWeight, size, semantic, align, children, ...otherProps } = props;
77
77
  const { theme } = useTheme();
78
- const { t } = useTranslation();
79
78
  // Check if there's a color override from parent context (e.g., Button)
80
79
  const contextColor = React.useContext(TextColorContext);
81
80
  // Use context color if provided, otherwise use theme default
@@ -100,7 +99,7 @@ export const StyledText = forwardRef((props, ref) => {
100
99
  const styleHasFontSize = flatStyle && "fontSize" in flatStyle;
101
100
  const styleHasLineHeight = flatStyle && "lineHeight" in flatStyle;
102
101
  const resolvedLineHeight = styleHasFontSize && !styleHasLineHeight ? undefined : lineHeight;
103
- const i18nText = tx ? String(t(tx, txOptions)) : text;
102
+ const i18nText = translateText(tx, text, txOptions);
104
103
  const content = i18nText || children;
105
104
  return (_jsx(RNText, { ref: ref, style: [
106
105
  {
@@ -0,0 +1,3 @@
1
+ export type TranslateFn = (key: string, options?: object) => string;
2
+ export declare function configureExpoUiI18n(fn: TranslateFn | null): void;
3
+ export declare function translateText(key?: string, text?: string, options?: object): string | undefined;
@@ -0,0 +1,11 @@
1
+ let translate = null;
2
+ export function configureExpoUiI18n(fn) {
3
+ translate = fn;
4
+ }
5
+ export function translateText(key, text, options) {
6
+ if (text)
7
+ return text;
8
+ if (!key)
9
+ return undefined;
10
+ return translate ? translate(key, options) : key;
11
+ }
@@ -1,2 +1,3 @@
1
1
  export * from "./animations";
2
2
  export * from "./haptics";
3
+ export * from "./i18n";
package/dist/lib/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./animations.js";
2
2
  export * from "./haptics.js";
3
+ export * from "./i18n.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrmeg/expo-ui",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "private": false,
5
5
  "description": "Reusable Expo and React Native UI primitives for MrMeg projects.",
6
6
  "keywords": [
@@ -105,9 +105,7 @@
105
105
  "expo": "~55.0.0",
106
106
  "expo-font": "~55.0.0",
107
107
  "expo-haptics": "~55.0.0",
108
- "i18next": ">=25.0.0 <27.0.0",
109
108
  "react": ">=19.2.0 <20.0.0",
110
- "react-i18next": ">=15.0.0 <18.0.0",
111
109
  "react-native": ">=0.83.0 <0.84.0",
112
110
  "react-native-gesture-handler": "~2.30.0",
113
111
  "react-native-reanimated": "~4.2.0",