@mrmeg/expo-ui 0.1.3 → 0.1.5
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 +14 -1
- package/README.md +28 -15
- package/dist/components/Button.d.ts +1 -1
- package/dist/components/Button.js +2 -2
- package/dist/components/Notification.js +4 -5
- package/dist/components/StyledText.d.ts +2 -2
- package/dist/components/StyledText.js +2 -3
- package/dist/lib/i18n.d.ts +3 -0
- package/dist/lib/i18n.js +11 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +1 -0
- package/package.json +1 -3
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,19 @@ 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 fallback text when provided and otherwise render the key
|
|
74
|
+
until the consumer opts in with a package-local translator. Package-owned
|
|
75
|
+
defaults such as notification titles stay human-readable without app i18n:
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { configureExpoUiI18n } from "@mrmeg/expo-ui/lib";
|
|
79
|
+
import { i18n } from "./i18n";
|
|
80
|
+
|
|
81
|
+
configureExpoUiI18n((key, options) => i18n.t(key, options));
|
|
82
|
+
```
|
|
83
|
+
|
|
71
84
|
## Theme And Text Rules
|
|
72
85
|
|
|
73
86
|
- 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.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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,18 @@ 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 its
|
|
120
|
+
fallback text when provided and otherwise renders the key; package-owned
|
|
121
|
+
defaults such as notification titles use readable fallback text. Consumers
|
|
122
|
+
that already use i18n can connect it once near app startup:
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
import { configureExpoUiI18n } from "@mrmeg/expo-ui/lib";
|
|
126
|
+
import { i18n } from "./i18n";
|
|
127
|
+
|
|
128
|
+
configureExpoUiI18n((key, options) => i18n.t(key, options));
|
|
129
|
+
```
|
|
130
|
+
|
|
119
131
|
## Component Guide
|
|
120
132
|
|
|
121
133
|
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 +348,14 @@ publishing instead:
|
|
|
336
348
|
1. In npm package settings for `@mrmeg/expo-ui`, add a trusted publisher:
|
|
337
349
|
GitHub Actions, owner `mrmeg`, repository `expo-template`, workflow
|
|
338
350
|
filename `publish-ui.yml`.
|
|
339
|
-
2.
|
|
340
|
-
and `ref=dev`.
|
|
351
|
+
2. Bump `packages/ui/package.json` in a commit and push it to `main`.
|
|
341
352
|
|
|
342
|
-
The workflow uses npm OIDC, not a checked-in token or local npm login.
|
|
343
|
-
the package version,
|
|
344
|
-
|
|
345
|
-
|
|
353
|
+
The workflow uses npm OIDC, not a checked-in token or local npm login. On push,
|
|
354
|
+
it reads the committed package version, skips cleanly if that version is already
|
|
355
|
+
published, otherwise runs the package gates and publishes from `packages/ui`.
|
|
356
|
+
The workflow can also be run manually with `version=patch` and `ref=main`; manual
|
|
357
|
+
runs bump the package version, update `bun.lock`, run the package gates, commit
|
|
358
|
+
the version bump, and publish.
|
|
346
359
|
|
|
347
360
|
Keep `repository.url` in `package.json` as
|
|
348
361
|
`git+https://github.com/mrmeg/expo-template.git`. npm trusted publishing checks
|
|
@@ -353,9 +366,9 @@ If npm trusted publishing is blocked by package settings, add an npm automation
|
|
|
353
366
|
or granular publish token to GitHub Actions secrets as `NPM_TOKEN` and rerun the
|
|
354
367
|
same workflow. The token is used only for the publish step.
|
|
355
368
|
|
|
356
|
-
If the workflow fails after the version is already bumped, rerun it with
|
|
357
|
-
exact current package version, for example `version=0.1.
|
|
358
|
-
do not bump again.
|
|
369
|
+
If the manual workflow fails after the version is already bumped, rerun it with
|
|
370
|
+
the exact current package version, for example `version=0.1.3`. Exact-version
|
|
371
|
+
reruns do not bump again.
|
|
359
372
|
|
|
360
373
|
Manual package checks:
|
|
361
374
|
|
|
@@ -20,7 +20,7 @@ export interface ButtonProps extends PressableProps {
|
|
|
20
20
|
*/
|
|
21
21
|
tx?: TextProps["tx"];
|
|
22
22
|
/**
|
|
23
|
-
* The text to display
|
|
23
|
+
* The text to display directly, or as fallback text when `tx` is provided.
|
|
24
24
|
*/
|
|
25
25
|
text?: TextProps["text"];
|
|
26
26
|
/**
|
|
@@ -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
|
-
]
|
|
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
|
|
142
|
+
return translateText("notification.error", "Error");
|
|
144
143
|
case "success":
|
|
145
|
-
return
|
|
144
|
+
return translateText("notification.success", "Success");
|
|
146
145
|
case "warning":
|
|
147
|
-
return
|
|
146
|
+
return translateText("notification.warning", "Warning");
|
|
148
147
|
case "info":
|
|
149
148
|
return "";
|
|
150
149
|
default:
|
|
@@ -53,7 +53,7 @@ export type TextProps = RNTextProps & {
|
|
|
53
53
|
*/
|
|
54
54
|
tx?: string;
|
|
55
55
|
/**
|
|
56
|
-
* The text to display
|
|
56
|
+
* The text to display directly, or as fallback text when `tx` is provided.
|
|
57
57
|
*/
|
|
58
58
|
text?: string;
|
|
59
59
|
/**
|
|
@@ -101,7 +101,7 @@ export declare const StyledText: React.ForwardRefExoticComponent<RNTextProps & {
|
|
|
101
101
|
*/
|
|
102
102
|
tx?: string;
|
|
103
103
|
/**
|
|
104
|
-
* The text to display
|
|
104
|
+
* The text to display directly, or as fallback text when `tx` is provided.
|
|
105
105
|
*/
|
|
106
106
|
text?: string;
|
|
107
107
|
/**
|
|
@@ -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 =
|
|
102
|
+
const i18nText = translateText(tx, text, txOptions);
|
|
104
103
|
const content = i18nText || children;
|
|
105
104
|
return (_jsx(RNText, { ref: ref, style: [
|
|
106
105
|
{
|
package/dist/lib/i18n.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
let translate = null;
|
|
2
|
+
export function configureExpoUiI18n(fn) {
|
|
3
|
+
translate = fn;
|
|
4
|
+
}
|
|
5
|
+
export function translateText(key, fallbackText, options) {
|
|
6
|
+
if (!key)
|
|
7
|
+
return fallbackText;
|
|
8
|
+
if (!translate)
|
|
9
|
+
return fallbackText ?? key;
|
|
10
|
+
return translate(key, options) ?? fallbackText ?? key;
|
|
11
|
+
}
|
package/dist/lib/index.d.ts
CHANGED
package/dist/lib/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrmeg/expo-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
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",
|