@mrmeg/expo-ui 0.25.1 → 0.27.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 +140 -0
- package/LLM_USAGE.md +15 -7
- package/README.md +94 -23
- package/dist/components/Avatar.d.ts +1 -1
- package/dist/components/BottomSheet.d.ts +24 -4
- package/dist/components/BottomSheet.js +37 -52
- package/dist/components/DropdownMenu.js +1 -1
- package/dist/components/Icon.d.ts +24 -22
- package/dist/components/Icon.js +26 -3
- package/dist/components/Notification.js +3 -3
- package/dist/components/Popover.js +1 -1
- package/dist/components/Select.js +1 -1
- package/dist/components/StatCard.d.ts +1 -1
- package/dist/components/StyledText.d.ts +6 -2
- package/dist/components/StyledText.js +10 -4
- package/dist/components/TextInput.js +35 -11
- package/dist/components/Tooltip.js +1 -1
- package/dist/components/iconRegistry.generated.d.ts +148 -0
- package/dist/components/iconRegistry.generated.js +296 -0
- package/dist/components/keyboardFocusRegistry.d.ts +10 -2
- package/dist/components/keyboardFocusRegistry.js +13 -5
- package/dist/components/nativeTextField.android.d.ts +3 -0
- package/dist/components/nativeTextField.android.js +226 -0
- package/dist/components/nativeTextField.d.ts +14 -0
- package/dist/components/nativeTextField.js +14 -0
- package/dist/design-system.json +151 -2
- package/dist/hooks/useResources.d.ts +5 -18
- package/dist/hooks/useResources.js +5 -43
- package/llms-full.md +4 -4
- package/llms.txt +4 -1
- package/package.json +4 -3
package/dist/design-system.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"package": "@mrmeg/expo-ui",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.27.0",
|
|
5
5
|
"tokens": {
|
|
6
6
|
"spacing": {
|
|
7
7
|
"entries": [
|
|
@@ -2232,5 +2232,154 @@
|
|
|
2232
2232
|
"variantValues": null,
|
|
2233
2233
|
"sizeValues": null
|
|
2234
2234
|
}
|
|
2235
|
-
]
|
|
2235
|
+
],
|
|
2236
|
+
"icons": {
|
|
2237
|
+
"names": [
|
|
2238
|
+
"archive",
|
|
2239
|
+
"arrow-down",
|
|
2240
|
+
"arrow-left",
|
|
2241
|
+
"arrow-right",
|
|
2242
|
+
"arrow-up",
|
|
2243
|
+
"award",
|
|
2244
|
+
"beer",
|
|
2245
|
+
"bell",
|
|
2246
|
+
"bell-off",
|
|
2247
|
+
"bold",
|
|
2248
|
+
"bookmark",
|
|
2249
|
+
"box",
|
|
2250
|
+
"briefcase",
|
|
2251
|
+
"calendar",
|
|
2252
|
+
"calendar-check",
|
|
2253
|
+
"camera",
|
|
2254
|
+
"camera-off",
|
|
2255
|
+
"chart-no-axes-column",
|
|
2256
|
+
"check",
|
|
2257
|
+
"chevron-down",
|
|
2258
|
+
"chevron-left",
|
|
2259
|
+
"chevron-right",
|
|
2260
|
+
"chevron-up",
|
|
2261
|
+
"circle-alert",
|
|
2262
|
+
"circle-check-big",
|
|
2263
|
+
"circle-plus",
|
|
2264
|
+
"circle-question-mark",
|
|
2265
|
+
"circle-stop",
|
|
2266
|
+
"circle-x",
|
|
2267
|
+
"clock",
|
|
2268
|
+
"cloud",
|
|
2269
|
+
"cloud-off",
|
|
2270
|
+
"code",
|
|
2271
|
+
"coffee",
|
|
2272
|
+
"compass",
|
|
2273
|
+
"copy",
|
|
2274
|
+
"credit-card",
|
|
2275
|
+
"crosshair",
|
|
2276
|
+
"database",
|
|
2277
|
+
"dollar-sign",
|
|
2278
|
+
"download",
|
|
2279
|
+
"droplet",
|
|
2280
|
+
"ellipsis-vertical",
|
|
2281
|
+
"external-link",
|
|
2282
|
+
"eye",
|
|
2283
|
+
"eye-off",
|
|
2284
|
+
"face-slightly-smiling",
|
|
2285
|
+
"file",
|
|
2286
|
+
"file-text",
|
|
2287
|
+
"folder",
|
|
2288
|
+
"gift",
|
|
2289
|
+
"git-branch",
|
|
2290
|
+
"git-pull-request",
|
|
2291
|
+
"globe",
|
|
2292
|
+
"grid-3x3",
|
|
2293
|
+
"hash",
|
|
2294
|
+
"heart",
|
|
2295
|
+
"hexagon",
|
|
2296
|
+
"house",
|
|
2297
|
+
"image",
|
|
2298
|
+
"inbox",
|
|
2299
|
+
"info",
|
|
2300
|
+
"italic",
|
|
2301
|
+
"key",
|
|
2302
|
+
"layers",
|
|
2303
|
+
"layout-grid",
|
|
2304
|
+
"link",
|
|
2305
|
+
"link-2",
|
|
2306
|
+
"list",
|
|
2307
|
+
"loader",
|
|
2308
|
+
"lock",
|
|
2309
|
+
"lock-open",
|
|
2310
|
+
"log-in",
|
|
2311
|
+
"log-out",
|
|
2312
|
+
"mail",
|
|
2313
|
+
"map",
|
|
2314
|
+
"map-pin",
|
|
2315
|
+
"maximize",
|
|
2316
|
+
"maximize-2",
|
|
2317
|
+
"menu",
|
|
2318
|
+
"message-circle",
|
|
2319
|
+
"message-square",
|
|
2320
|
+
"mic",
|
|
2321
|
+
"mic-off",
|
|
2322
|
+
"minus",
|
|
2323
|
+
"monitor",
|
|
2324
|
+
"moon",
|
|
2325
|
+
"music",
|
|
2326
|
+
"navigation",
|
|
2327
|
+
"package",
|
|
2328
|
+
"panel-left",
|
|
2329
|
+
"panels-top-left",
|
|
2330
|
+
"party-popper",
|
|
2331
|
+
"pen",
|
|
2332
|
+
"pen-line",
|
|
2333
|
+
"pencil",
|
|
2334
|
+
"percent",
|
|
2335
|
+
"phone",
|
|
2336
|
+
"play",
|
|
2337
|
+
"plus",
|
|
2338
|
+
"printer",
|
|
2339
|
+
"radio",
|
|
2340
|
+
"refresh-cw",
|
|
2341
|
+
"repeat",
|
|
2342
|
+
"rotate-cw",
|
|
2343
|
+
"search",
|
|
2344
|
+
"send",
|
|
2345
|
+
"server",
|
|
2346
|
+
"settings",
|
|
2347
|
+
"share",
|
|
2348
|
+
"share-2",
|
|
2349
|
+
"shield",
|
|
2350
|
+
"shield-off",
|
|
2351
|
+
"shopping-bag",
|
|
2352
|
+
"shopping-cart",
|
|
2353
|
+
"shuffle",
|
|
2354
|
+
"slash",
|
|
2355
|
+
"sliders-horizontal",
|
|
2356
|
+
"sliders-vertical",
|
|
2357
|
+
"smartphone",
|
|
2358
|
+
"square",
|
|
2359
|
+
"square-check-big",
|
|
2360
|
+
"square-pen",
|
|
2361
|
+
"star",
|
|
2362
|
+
"sun",
|
|
2363
|
+
"tag",
|
|
2364
|
+
"terminal",
|
|
2365
|
+
"thumbs-up",
|
|
2366
|
+
"trash",
|
|
2367
|
+
"trending-down",
|
|
2368
|
+
"trending-up",
|
|
2369
|
+
"triangle-alert",
|
|
2370
|
+
"truck",
|
|
2371
|
+
"underline",
|
|
2372
|
+
"upload",
|
|
2373
|
+
"user",
|
|
2374
|
+
"user-check",
|
|
2375
|
+
"user-minus",
|
|
2376
|
+
"user-plus",
|
|
2377
|
+
"users",
|
|
2378
|
+
"video",
|
|
2379
|
+
"wifi-off",
|
|
2380
|
+
"wrench",
|
|
2381
|
+
"x",
|
|
2382
|
+
"zap"
|
|
2383
|
+
]
|
|
2384
|
+
}
|
|
2236
2385
|
}
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Registers the Feather icon font in expo-font's server store during
|
|
3
|
-
* export-time rendering (`expo export` renders every route's HTML shell in
|
|
4
|
-
* Node, where the module-scope client load above is guarded off).
|
|
5
|
-
*
|
|
6
|
-
* Must be called from a **render body** (see `useResources` below): expo-font
|
|
7
|
-
* scopes its server store per render context, so a module-scope registration
|
|
8
|
-
* lands in at most one route's shell. Registering during render means every
|
|
9
|
-
* exported shell emits the `@font-face` rule and renders `<Icon>` glyphs —
|
|
10
|
-
* without it the shell contains an EMPTY icon text node while the client's
|
|
11
|
-
* first render (font pre-loaded above) contains the glyph, and React throws
|
|
12
|
-
* hydration error #418 on every cold load and regenerates the tree.
|
|
13
|
-
*
|
|
14
|
-
* No-op on native and on client web (both already covered: native by the
|
|
15
|
-
* effect below, client web by the module-scope call above).
|
|
16
|
-
*/
|
|
17
|
-
export declare function ensureIconFontRegistered(): void;
|
|
18
1
|
interface LoadResourcesResult {
|
|
19
2
|
loaded: boolean;
|
|
20
3
|
error: Error | null;
|
|
@@ -27,12 +10,16 @@ interface LoadResourcesResult {
|
|
|
27
10
|
* font files. Web loads Inter from Google Fonts as a single CSS family;
|
|
28
11
|
* weight differentiation there comes from a numeric fontWeight instead.
|
|
29
12
|
*
|
|
13
|
+
* Icons need nothing here: `Icon` renders `lucide-react-native` SVGs, which
|
|
14
|
+
* have no font face to register on any platform, so server-rendered HTML
|
|
15
|
+
* carries the glyph markup and hydrates without a font round trip.
|
|
16
|
+
*
|
|
30
17
|
* A host app that overrides the sans-serif families via `setFonts` owns
|
|
31
18
|
* loading its own faces (typically through `expo-font`), so the Inter fetch
|
|
32
19
|
* is skipped entirely — nothing would reference those files. For the skip to
|
|
33
20
|
* apply, call `setFonts` before this hook mounts (module scope or ahead of
|
|
34
21
|
* rendering the root); a later call still re-skins text, it just doesn't
|
|
35
|
-
* un-download Inter.
|
|
22
|
+
* un-download Inter.
|
|
36
23
|
*/
|
|
37
24
|
export declare const useResources: () => LoadResourcesResult;
|
|
38
25
|
export {};
|
|
@@ -1,46 +1,8 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
2
|
import * as Font from "expo-font";
|
|
3
|
-
import Feather from "@expo/vector-icons/Feather";
|
|
4
3
|
import { Inter_400Regular, Inter_500Medium, Inter_600SemiBold, Inter_700Bold, } from "@expo-google-fonts/inter";
|
|
5
4
|
import { Platform } from "react-native";
|
|
6
5
|
import { useThemeStore } from "../state/themeStore.js";
|
|
7
|
-
// Eager, module-scope load that primes the Feather font before first render:
|
|
8
|
-
// on web it synchronously injects the @font-face rule, so icons have their
|
|
9
|
-
// glyphs on the first paint; on native it just starts the fetch ahead of the
|
|
10
|
-
// effect below (which then no-ops for the already-loaded font).
|
|
11
|
-
//
|
|
12
|
-
// Guarded on `window` because web bundles are also evaluated in Node when
|
|
13
|
-
// `expo export` renders the HTML shell, where there is no document to inject
|
|
14
|
-
// into.
|
|
15
|
-
if (typeof window !== "undefined") {
|
|
16
|
-
void Font.loadAsync(Feather.font);
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Registers the Feather icon font in expo-font's server store during
|
|
20
|
-
* export-time rendering (`expo export` renders every route's HTML shell in
|
|
21
|
-
* Node, where the module-scope client load above is guarded off).
|
|
22
|
-
*
|
|
23
|
-
* Must be called from a **render body** (see `useResources` below): expo-font
|
|
24
|
-
* scopes its server store per render context, so a module-scope registration
|
|
25
|
-
* lands in at most one route's shell. Registering during render means every
|
|
26
|
-
* exported shell emits the `@font-face` rule and renders `<Icon>` glyphs —
|
|
27
|
-
* without it the shell contains an EMPTY icon text node while the client's
|
|
28
|
-
* first render (font pre-loaded above) contains the glyph, and React throws
|
|
29
|
-
* hydration error #418 on every cold load and regenerates the tree.
|
|
30
|
-
*
|
|
31
|
-
* No-op on native and on client web (both already covered: native by the
|
|
32
|
-
* effect below, client web by the module-scope call above).
|
|
33
|
-
*/
|
|
34
|
-
export function ensureIconFontRegistered() {
|
|
35
|
-
if (Platform.OS !== "web" || typeof window !== "undefined")
|
|
36
|
-
return;
|
|
37
|
-
if (Font.isLoaded("feather"))
|
|
38
|
-
return;
|
|
39
|
-
// Synchronous on server-web (expo-font routes to registerStaticFont). No
|
|
40
|
-
// `void`/try-catch: a throw here means we're outside a render scope, which
|
|
41
|
-
// must be loud.
|
|
42
|
-
Font.loadAsync(Feather.font);
|
|
43
|
-
}
|
|
44
6
|
// The four static Inter weights StyledText's native family keys point at
|
|
45
7
|
// (see constants/fonts.ts). Native-only: web never renders these family names
|
|
46
8
|
// ("Inter_400Regular" etc.) — fontFamilies.sansSerif resolves every weight to
|
|
@@ -86,17 +48,18 @@ function ensureWebFontStylesheet() {
|
|
|
86
48
|
* font files. Web loads Inter from Google Fonts as a single CSS family;
|
|
87
49
|
* weight differentiation there comes from a numeric fontWeight instead.
|
|
88
50
|
*
|
|
51
|
+
* Icons need nothing here: `Icon` renders `lucide-react-native` SVGs, which
|
|
52
|
+
* have no font face to register on any platform, so server-rendered HTML
|
|
53
|
+
* carries the glyph markup and hydrates without a font round trip.
|
|
54
|
+
*
|
|
89
55
|
* A host app that overrides the sans-serif families via `setFonts` owns
|
|
90
56
|
* loading its own faces (typically through `expo-font`), so the Inter fetch
|
|
91
57
|
* is skipped entirely — nothing would reference those files. For the skip to
|
|
92
58
|
* apply, call `setFonts` before this hook mounts (module scope or ahead of
|
|
93
59
|
* rendering the root); a later call still re-skins text, it just doesn't
|
|
94
|
-
* un-download Inter.
|
|
60
|
+
* un-download Inter.
|
|
95
61
|
*/
|
|
96
62
|
export const useResources = () => {
|
|
97
|
-
// Export-time renders need the icon font registered per render scope; the
|
|
98
|
-
// module-scope call only reaches the client. See ensureIconFontRegistered.
|
|
99
|
-
ensureIconFontRegistered();
|
|
100
63
|
const [loaded, setLoaded] = useState(false);
|
|
101
64
|
const [error, setError] = useState(null);
|
|
102
65
|
useEffect(() => {
|
|
@@ -107,7 +70,6 @@ export const useResources = () => {
|
|
|
107
70
|
async function loadResourcesAndDataAsync() {
|
|
108
71
|
try {
|
|
109
72
|
const fontPromise = Promise.all([
|
|
110
|
-
Font.loadAsync(Feather.font),
|
|
111
73
|
sansSerifOverridden ? Promise.resolve() : loadNativeInterFonts(),
|
|
112
74
|
sansSerifOverridden ? Promise.resolve() : ensureWebFontStylesheet(),
|
|
113
75
|
]);
|
package/llms-full.md
CHANGED
|
@@ -36,8 +36,8 @@ import { Button, colors, UIProvider, useTheme } from "@mrmeg/expo-ui";
|
|
|
36
36
|
## App Setup
|
|
37
37
|
|
|
38
38
|
Call `useResources()` once near the Expo app root; it resolves `{ loaded, error }`
|
|
39
|
-
and loads the
|
|
40
|
-
|
|
39
|
+
and loads the four static Inter weights (web gets a single Google Fonts Inter
|
|
40
|
+
stylesheet). Icons are `lucide-react-native` SVGs, so there is no icon font to load.
|
|
41
41
|
|
|
42
42
|
Mount `UIProvider` once near the root. It owns the package `Notification`,
|
|
43
43
|
`StatusBar`, default `@rn-primitives` portal host, and native keyboard-avoiding
|
|
@@ -103,7 +103,7 @@ before creating a new app-local primitive.
|
|
|
103
103
|
| `AnimatedView` | Entrance and visibility animation | Keep simple reveal effects in this wrapper. |
|
|
104
104
|
| `Avatar`, `AvatarGroup` | Profile images with initials/icon fallback | Pass both `source` and `name`; set `size`/`shape` on the group, not per child. |
|
|
105
105
|
| `Badge` | Short status labels | Prefer over custom pill views. |
|
|
106
|
-
| `BottomSheet` | Mobile-first modal sheets | Requires root `UIProvider`. Native sheet via `@expo/ui`; `swipeEnabled`, `avoidKeyboard`, `dismissKeyboardOnDrag` are accepted but ignored. |
|
|
106
|
+
| `BottomSheet` | Mobile-first modal sheets | Requires root `UIProvider`. Native sheet via `@expo/ui`; `swipeEnabled`, `avoidKeyboard`, `dismissKeyboardOnDrag` are accepted but ignored (the iOS sheet presentation and Android Material3 keep content above the keyboard; never nest a `KeyboardAvoidingView` in a sheet). |
|
|
107
107
|
| `Button` | Commands and CTAs | Use `preset`, not `variant`; heights are 28/32/40. |
|
|
108
108
|
| `Card` | Individual framed content groups | Do not wrap whole page sections in cards. |
|
|
109
109
|
| `Carousel` | Small known set of horizontally snapping slides | Renders every child (no virtualization); fractional `itemWidth` measures the viewport until first layout. |
|
|
@@ -115,7 +115,7 @@ before creating a new app-local primitive.
|
|
|
115
115
|
| `DropdownMenu` | Menus and command lists | Requires root `UIProvider` portal host. |
|
|
116
116
|
| `EmptyState` | No-data or recoverable error regions | Props: `icon`, `title`, `description`, `actionLabel`, `onAction`, `actionPreset`. |
|
|
117
117
|
| `ErrorBoundary` | React render error fallback | Use for route or feature boundaries. |
|
|
118
|
-
| `Icon` |
|
|
118
|
+
| `Icon` | Lucide or custom icons with theme tokens | `color` takes a theme color name; pass `decorative` to hide from a11y. |
|
|
119
119
|
| `InputOTP` | Verification code entry | Prefer over manually managed text input groups. |
|
|
120
120
|
| `Item` | List / settings rows | Applies the row density tokens and a 44px native hit area (40px on web). |
|
|
121
121
|
| `KeyboardAvoidingView` | Native keyboard-aware roots, composer footers, form-heavy subtrees | `UIProvider` already mounts one root; use directly only for custom subtrees. |
|
package/llms.txt
CHANGED
|
@@ -27,7 +27,10 @@ mount react-native-keyboard-controller's KeyboardProvider above UIProvider.
|
|
|
27
27
|
|
|
28
28
|
BottomSheet renders the platform's native sheet via @expo/ui, which owns
|
|
29
29
|
gestures and keyboard avoidance; swipeEnabled, avoidKeyboard, and
|
|
30
|
-
dismissKeyboardOnDrag are accepted but ignored.
|
|
30
|
+
dismissKeyboardOnDrag are accepted but ignored. On iOS the sheet presentation
|
|
31
|
+
lifts or shrinks the hosted content so Footer and Body end at the keyboard's
|
|
32
|
+
top (device-verified); Android avoidance is Material3's. Do not nest a
|
|
33
|
+
KeyboardAvoidingView inside a sheet.
|
|
31
34
|
|
|
32
35
|
Use useTheme(), useStyles(), semantic tokens, StyledText, and package controls.
|
|
33
36
|
Do not add app-local Appearance or matchMedia listeners for theme sync. On web
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrmeg/expo-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Reusable Expo and React Native UI primitives for MrMeg projects.",
|
|
6
6
|
"keywords": [
|
|
@@ -86,13 +86,12 @@
|
|
|
86
86
|
"scripts": {
|
|
87
87
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
88
88
|
"test": "jest --config ../../jest.config.js packages/ui/src --runInBand --watchman=false && bun run check:forbidden-imports",
|
|
89
|
-
"build": "rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/fix-package-esm.mjs ui && node ../../scripts/build-design-system-manifest.mjs ui && bun run check:forbidden-imports",
|
|
89
|
+
"build": "node ../../scripts/build-icon-registry.mjs && rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/fix-package-esm.mjs ui && node ../../scripts/build-design-system-manifest.mjs ui && bun run check:forbidden-imports",
|
|
90
90
|
"check:forbidden-imports": "node ../../scripts/check-ui-forbidden-imports.mjs",
|
|
91
91
|
"publish:dry-run": "bun pm pack --dry-run"
|
|
92
92
|
},
|
|
93
93
|
"dependencies": {
|
|
94
94
|
"@expo-google-fonts/inter": "^0.4.2",
|
|
95
|
-
"@expo/vector-icons": ">=15.0.0 <16.0.0",
|
|
96
95
|
"@rn-primitives/accordion": "~1.5.2",
|
|
97
96
|
"@rn-primitives/alert-dialog": "~1.5.2",
|
|
98
97
|
"@rn-primitives/checkbox": "~1.5.2",
|
|
@@ -118,12 +117,14 @@
|
|
|
118
117
|
"expo": ">=56.0.0 <59.0.0 || >=58.0.0-0 <59.0.0",
|
|
119
118
|
"expo-font": ">=56.0.0 <59.0.0 || >=58.0.0-0 <59.0.0",
|
|
120
119
|
"expo-haptics": ">=56.0.0 <59.0.0 || >=58.0.0-0 <59.0.0",
|
|
120
|
+
"lucide-react-native": ">=1.46.0 <2.0.0",
|
|
121
121
|
"react": ">=19.2.0 <20.0.0",
|
|
122
122
|
"react-native": ">=0.85.0 <0.89.0 || >=0.88.0-0 <0.89.0",
|
|
123
123
|
"react-native-gesture-handler": ">=2.30.0 <4.0.0",
|
|
124
124
|
"react-native-keyboard-controller": ">=1.21.0 <2.0.0",
|
|
125
125
|
"react-native-safe-area-context": ">=5.6.0 <6.0.0",
|
|
126
126
|
"react-native-screens": ">=4.23.0 <5.0.0",
|
|
127
|
+
"react-native-svg": ">=15.0.0 <16.0.0",
|
|
127
128
|
"react-native-web": ">=0.21.0 <0.22.0",
|
|
128
129
|
"zustand": ">=5.0.0 <6.0.0"
|
|
129
130
|
},
|