@mrmeg/expo-ui 0.1.1 → 0.1.3

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 ADDED
@@ -0,0 +1,202 @@
1
+ # @mrmeg/expo-ui LLM Usage Guide
2
+
3
+ This file ships in the npm package. In a consumer repo, read it from
4
+ `node_modules/@mrmeg/expo-ui/LLM_USAGE.md` before building app UI.
5
+
6
+ ## First Rule
7
+
8
+ Do not recreate primitives that this package already provides. Import from
9
+ `@mrmeg/expo-ui` and compose the exported components in the app.
10
+
11
+ ## Stable Import Paths
12
+
13
+ ```tsx
14
+ import { Button, StyledText, UIProvider } from "@mrmeg/expo-ui/components";
15
+ import { Button as ButtonDirect } from "@mrmeg/expo-ui/components/Button";
16
+ import { colors, spacing, typography } from "@mrmeg/expo-ui/constants";
17
+ import { useResources, useTheme } from "@mrmeg/expo-ui/hooks";
18
+ import { globalUIStore, useThemeStore } from "@mrmeg/expo-ui/state";
19
+ import { hapticLight } from "@mrmeg/expo-ui/lib";
20
+ ```
21
+
22
+ The root barrel also exports the public surface:
23
+
24
+ ```tsx
25
+ import { Button, UIProvider, colors, useTheme } from "@mrmeg/expo-ui";
26
+ ```
27
+
28
+ Use only exported package paths: root, `components`, `components/*`,
29
+ `constants`, `constants/*`, `hooks`, `hooks/*`, `state`, and `lib`. Do not
30
+ import from `@mrmeg/expo-ui/dist/*` or from a source checkout path.
31
+
32
+ ## Required App Setup
33
+
34
+ Call `useResources()` once near the Expo app root. Mount `UIProvider` once
35
+ near the root when the app uses package feedback or overlay components.
36
+ `UIProvider` owns the package `Notification`, `StatusBar`, and default
37
+ `@rn-primitives` portal host.
38
+
39
+ ```tsx
40
+ import { ThemeProvider } from "@react-navigation/native";
41
+ import { UIProvider } from "@mrmeg/expo-ui/components";
42
+ import { colors } from "@mrmeg/expo-ui/constants";
43
+ import { useResources, useTheme } from "@mrmeg/expo-ui/hooks";
44
+
45
+ export function RootLayout() {
46
+ const { scheme } = useTheme();
47
+ const { loaded } = useResources();
48
+
49
+ if (!loaded) return null;
50
+
51
+ return (
52
+ <ThemeProvider
53
+ value={{
54
+ dark: colors[scheme ?? "light"].dark,
55
+ colors: colors[scheme ?? "light"].navigation,
56
+ fonts: colors[scheme ?? "light"].fonts,
57
+ }}
58
+ >
59
+ <UIProvider>
60
+ {/* App navigation goes here. */}
61
+ </UIProvider>
62
+ </ThemeProvider>
63
+ );
64
+ }
65
+ ```
66
+
67
+ `UIProvider` mounts the default portal host required before using `Dialog`,
68
+ `AlertDialog`, `BottomSheet`, `Drawer`, `DropdownMenu`, `Popover`,
69
+ `SelectContent`, or `Tooltip`.
70
+
71
+ ## Theme And Text Rules
72
+
73
+ - Use `useTheme()` and semantic tokens instead of hardcoded colors.
74
+ - Use `StyledText` or its semantic aliases instead of raw `Text` for app UI.
75
+ - Use `Button.preset`, not `variant`, for buttons.
76
+ - Use `globalUIStore` plus root-mounted `UIProvider` for transient global feedback.
77
+ - Keep app monitoring, auth, API, and domain behavior outside this package.
78
+
79
+ Useful theme tokens include:
80
+
81
+ ```tsx
82
+ theme.colors.background;
83
+ theme.colors.foreground;
84
+ theme.colors.card;
85
+ theme.colors.border;
86
+ theme.colors.primary;
87
+ theme.colors.secondary;
88
+ theme.colors.accent;
89
+ theme.colors.mutedForeground;
90
+ theme.colors.destructive;
91
+ theme.colors.success;
92
+ theme.colors.warning;
93
+ ```
94
+
95
+ Token intent:
96
+
97
+ - `primary`: neutral action color
98
+ - `secondary`: neutral secondary surface
99
+ - `accent`: teal highlight color
100
+
101
+ ## Component Use-Case Index
102
+
103
+ Use this table before creating a new app-local primitive.
104
+
105
+ | Component | Use For | Prefer It Instead Of | Common Example Use Cases |
106
+ |-----------|---------|----------------------|--------------------------|
107
+ | `Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent` | Multi-section disclosure | Custom FAQ/settings expanders | FAQ lists, grouped settings, help sections |
108
+ | `Alert` | Cross-platform imperative alerts | Direct `window.alert` or duplicated RN/web branching | Confirm destructive actions, native alert dialogs |
109
+ | `AnimatedView` | Entrance and visibility animation | Hand-rolled Reanimated wrappers | Staggered list rows, revealed panels, animated empty states |
110
+ | `Badge` | Short status labels | Custom pill `View` + `Text` | Draft/active states, counts, plan labels, role tags |
111
+ | `BottomSheet` | Mobile-first modal sheets | Custom absolute-position sheets | Action pickers, mobile filters, quick edit forms |
112
+ | `Button` | Commands and CTAs | Pressable plus custom text styling | Submit, save, cancel, delete, navigation CTAs |
113
+ | `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter` | Framed content groups | Ad hoc bordered panels | List items, pricing plans, settings sections, summaries |
114
+ | `Checkbox` | Boolean selection | Custom checkmark controls | Terms consent, checklist items, multi-select filters |
115
+ | `Collapsible`, `CollapsibleTrigger`, `CollapsibleContent` | One-off disclosure | Local animated height wrappers | Advanced settings, hidden helper text |
116
+ | `Dialog`, `AlertDialog` | Modal decisions and custom modal content | Custom modal overlays | Confirm delete, edit profile, invite user |
117
+ | `DismissKeyboard` | Tap-away keyboard dismissal | Screen-level keyboard handling | Forms, search screens, sign-in screens |
118
+ | `Drawer` | Side panels and drawer navigation | Custom sliding panels | Filter drawer, app navigation drawer, inspector panel |
119
+ | `DropdownMenu` | Menus and command lists | Homemade popover menus | Row actions, account menu, sort menu |
120
+ | `EmptyState` | No-data or recoverable error regions | One-off empty placeholders | Empty inbox, no search results, failed list load |
121
+ | `ErrorBoundary` | React render error fallback | Unhandled screen crashes | Route-level fallback, feature boundary |
122
+ | `Icon` | Feather or custom icons with theme tokens | Raw vector icons with hardcoded colors | Button accessories, empty-state icons, menu icons |
123
+ | `InputOTP` | Verification code entry | Multiple manually managed text inputs | Email codes, SMS codes, MFA, invite codes |
124
+ | `Label` | Accessible form labels | Plain styled text labels | Required labels, disabled labels, field group labels |
125
+ | `MaxWidthContainer` | Centered responsive width | Per-screen max-width wrappers | Web pages, tablet layouts, settings forms |
126
+ | `Notification` | Global toast surface | Screen-local toast state | Saved/error/sync notifications, bottom-position alerts |
127
+ | `Popover` | Anchored contextual content | Custom anchored views | Inline help, quick previews, contextual controls |
128
+ | `Progress` | Determinate or indeterminate progress | Layout-shifting spinners for progress regions | Upload progress, onboarding completion |
129
+ | `RadioGroup`, `RadioGroupItem` | Mutually exclusive choices | Custom radio rows | Plan interval, visibility choice, survey answer |
130
+ | `Select` | Option menus | Custom dropdowns | Country picker, category selector, status selector |
131
+ | `Separator` | Horizontal or vertical dividers | Border-only spacer views | Menu dividers, section dividers, card dividers |
132
+ | `Skeleton`, `SkeletonText`, `SkeletonAvatar`, `SkeletonCard` | Loading placeholders | Blank space or generic spinners | List loading, profile card loading, dashboard placeholders |
133
+ | `Slider` | Numeric value selection | Custom pan gesture track | Volume, percentage, rating, threshold settings |
134
+ | `StatusBar` | Theme-aware native status bar | Per-screen status-bar duplication | Root layout status styling |
135
+ | `StyledText` and text aliases | Theme-aware typography | Raw `Text` with hardcoded styles | Titles, headings, labels, body copy, captions |
136
+ | `Switch` | Binary settings | Custom toggle switches | Enable notifications, privacy setting, feature toggles |
137
+ | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent` | In-page tabbed views | Custom segmented/tab controls | Profile sections, report views, settings categories |
138
+ | `TextInput` | Text entry | Raw `TextInput` with repeated label/error code | Email/password, search, numeric input, multiline notes |
139
+ | `Toggle`, `ToggleIcon` | Pressed/unpressed control | Button with local selected styling | Favorite, mute, bold/italic, view mode button |
140
+ | `ToggleGroup`, `ToggleGroupItem`, `ToggleGroupIcon` | Single or multi toggle groups | Custom segmented controls | Alignment, formatting toolbar, filter chips |
141
+ | `Tooltip` | Short hover/focus help | Persistent helper text or custom hover cards | Icon button labels, field hints, disabled action explanations |
142
+
143
+ ## Component Selection Rules
144
+
145
+ - Use `Button` for commands, `Toggle` for one pressed state, `ToggleGroup` for related pressed states, and `Switch` for binary settings.
146
+ - Use `RadioGroup` for small mutually exclusive choices and `Select` for longer option sets.
147
+ - Use `Dialog` for blocking decisions, `Popover` for contextual controls, `Tooltip` for short explanations, and `DropdownMenu` for action lists.
148
+ - Use `Card` for individual repeated or framed items, not as a wrapper around full page sections.
149
+ - Use `EmptyState` for no-data or recoverable error regions.
150
+ - Use `Skeleton` for loading content with stable layout.
151
+ - Use `Progress` for real progress or indeterminate long-running work.
152
+
153
+ ## Minimal Examples
154
+
155
+ ```tsx
156
+ import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from "@mrmeg/expo-ui/components";
157
+
158
+ <Card variant="outline">
159
+ <CardHeader>
160
+ <CardTitle>Subscription</CardTitle>
161
+ <Badge variant="secondary">Active</Badge>
162
+ </CardHeader>
163
+ <CardContent>
164
+ <Button preset="default" fullWidth>
165
+ Manage billing
166
+ </Button>
167
+ </CardContent>
168
+ </Card>
169
+ ```
170
+
171
+ ```tsx
172
+ import { Button, Switch, TextInput } from "@mrmeg/expo-ui/components";
173
+
174
+ <TextInput
175
+ label="Email"
176
+ placeholder="you@example.com"
177
+ autoCapitalize="none"
178
+ keyboardType="email-address"
179
+ errorText={emailError}
180
+ />
181
+
182
+ <Switch checked={enabled} onCheckedChange={setEnabled} variant="ios" />
183
+
184
+ <Button preset="default" size="lg" fullWidth loading={isSubmitting}>
185
+ Continue
186
+ </Button>
187
+ ```
188
+
189
+ ```tsx
190
+ import { EmptyState, Progress, SkeletonCard } from "@mrmeg/expo-ui/components";
191
+ import { globalUIStore } from "@mrmeg/expo-ui/state";
192
+
193
+ {isLoading ? <SkeletonCard /> : null}
194
+ <Progress value={65} variant="accent" />
195
+ <EmptyState icon="inbox" title="No messages" description="New messages will appear here." />
196
+
197
+ globalUIStore.getState().show({
198
+ type: "success",
199
+ title: "Saved",
200
+ messages: ["Your changes were saved."],
201
+ });
202
+ ```
package/README.md CHANGED
@@ -8,6 +8,14 @@ package, not a generally supported open-source UI library. The package is
8
8
  published as `UNLICENSED`; use outside MrMeg projects requires explicit
9
9
  permission.
10
10
 
11
+ ## For LLMs And Coding Agents
12
+
13
+ When this package is installed from npm, read
14
+ `node_modules/@mrmeg/expo-ui/LLM_USAGE.md` before creating app-local UI
15
+ primitives. That file is shipped in the npm package and gives the short
16
+ component-selection rules, import paths, setup requirements, and examples that
17
+ help agents choose existing package components instead of rebuilding them.
18
+
11
19
  ## Install
12
20
 
13
21
  Install from npm after publishing:
@@ -19,17 +27,22 @@ bun add @mrmeg/expo-ui
19
27
  Consumers must also install the peer dependencies listed in `package.json`.
20
28
  The tested baseline is Expo SDK 55 with React 19.2, React Native 0.83,
21
29
  React Native Web 0.21, Reanimated 4.2, Worklets 0.7, and
22
- `@rn-primitives/*` 1.4. Start consumer apps from the same Expo SDK family
23
- or update the package and peer ranges deliberately. Keep npm auth tokens in
24
- developer or CI configuration, not in this repository.
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.
25
36
 
26
37
  ## Imports
27
38
 
28
39
  ```tsx
29
- import { Button, StyledText } from "@mrmeg/expo-ui/components";
40
+ import { Button, StyledText, UIProvider } from "@mrmeg/expo-ui/components";
30
41
  import { Button as ButtonDirect } from "@mrmeg/expo-ui/components/Button";
31
42
  import { colors, spacing, typography } from "@mrmeg/expo-ui/constants";
43
+ import { colors as colorsDirect } from "@mrmeg/expo-ui/constants/colors";
32
44
  import { useResources, useTheme } from "@mrmeg/expo-ui/hooks";
45
+ import { useTheme as useThemeDirect } from "@mrmeg/expo-ui/hooks/useTheme";
33
46
  import { globalUIStore, useThemeStore } from "@mrmeg/expo-ui/state";
34
47
  import { hapticLight } from "@mrmeg/expo-ui/lib";
35
48
  ```
@@ -37,7 +50,7 @@ import { hapticLight } from "@mrmeg/expo-ui/lib";
37
50
  The root barrel also exports the public surface:
38
51
 
39
52
  ```tsx
40
- import { Button, colors, useTheme } from "@mrmeg/expo-ui";
53
+ import { Button, UIProvider, colors, useTheme } from "@mrmeg/expo-ui";
41
54
  ```
42
55
 
43
56
  ## Theme System
@@ -105,33 +118,88 @@ Useful `StyledText` props:
105
118
 
106
119
  ## Component Guide
107
120
 
108
- All components are exported from `@mrmeg/expo-ui/components`; direct imports such as `@mrmeg/expo-ui/components/Button` are supported.
109
-
110
- Layout:
111
-
112
- - `AnimatedView` - Reanimated visibility/entrance wrapper.
113
- - `Card` - Framed content with `variant`: `default`, `outline`, `ghost`; includes `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`.
114
- - `MaxWidthContainer` - Centered responsive content width.
115
- - `Separator` - Theme-aware divider.
116
- - `DismissKeyboard` - Tap-away keyboard dismissal wrapper.
117
-
118
- Forms and actions:
119
-
120
- - `Button` - Commands with `preset`: `default` for primary neutral actions, `secondary` for neutral secondary actions, plus `outline`, `ghost`, `link`, and `destructive`; `size`: `sm`, `md`, `lg`; supports `loading`, `fullWidth`, accessories, and shadows.
121
- - `TextInput` - Inputs with `variant`: `outline`, `filled`, `underlined`; `size`: `sm`, `md`, `lg`; supports labels, helper/error text, clear/password affordances, and left/right elements.
122
- - `Checkbox`, `RadioGroup`, `Select`, `Switch`, `Toggle`, `ToggleGroup`, `InputOTP`, `Slider`, `Label` - Form controls built on package tokens and `@rn-primitives` where applicable.
123
-
124
- Feedback:
125
-
126
- - `Alert`, `Badge`, `Notification`, `Progress`, `Skeleton`, `SkeletonText`, `SkeletonAvatar`, `SkeletonCard`, `EmptyState`, `StatusBar`.
127
- - Mount `Notification` once near the app root; trigger it with `globalUIStore`.
128
-
129
- Overlays and navigation:
130
-
131
- - `Dialog`, `BottomSheet`, `Drawer`, `DropdownMenu`, `Popover`, `Tooltip` require `PortalHost` near the app root.
132
- - `Tabs`, `Accordion`, and `Collapsible` cover in-page navigation and disclosure.
133
-
134
- Example:
121
+ 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.
122
+
123
+ ### LLM Component Use-Case Index
124
+
125
+ | Component | Use For | Prefer It Instead Of | Common Example Use Cases |
126
+ |-----------|---------|----------------------|--------------------------|
127
+ | `Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent` | Multi-section disclosure | Custom FAQ/settings expanders | FAQ lists, grouped settings, help sections, dense detail pages |
128
+ | `Alert` | Cross-platform imperative alerts | Direct `window.alert` or duplicated RN/web branching | Confirm destructive actions, native alert dialogs, simple blocking messages |
129
+ | `AnimatedView` | Entrance and visibility animation | Hand-rolled Reanimated wrappers | Staggered list rows, revealed panels, animated empty states |
130
+ | `Badge` | Short status labels | Custom pill `View` + `Text` | Draft/active states, counts, plan labels, role tags |
131
+ | `BottomSheet` | Mobile-first modal sheets | Custom absolute-position sheets | Action pickers, mobile filters, quick edit forms, contextual details |
132
+ | `Button` | Commands and CTAs | Pressable plus custom text styling | Submit, save, cancel, delete, navigation CTAs, icon-accessory buttons |
133
+ | `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter` | Framed content groups | Ad hoc bordered panels | List items, pricing plans, settings sections, summaries, dashboards |
134
+ | `Checkbox` | Boolean selection | Custom checkmark controls | Terms consent, checklist items, multi-select filters, notification opt-ins |
135
+ | `Collapsible`, `CollapsibleTrigger`, `CollapsibleContent` | One-off disclosure | Local animated height wrappers | Advanced settings, hidden helper text, optional details |
136
+ | `Dialog`, `AlertDialog` | Modal decisions and custom modal content | Custom modal overlays | Confirm delete, edit profile, invite user, blocking warnings |
137
+ | `DismissKeyboard` | Tap-away keyboard dismissal | Screen-level keyboard handling | Forms, search screens, sign-in screens |
138
+ | `Drawer` | Side panels and drawer navigation | Custom sliding panels | Filter drawer, app navigation drawer, inspector panel |
139
+ | `DropdownMenu` | Menus and command lists | Homemade popover menus | Row actions, account menu, sort menu, checkbox/radio menu groups |
140
+ | `EmptyState` | No-data or recoverable error regions | One-off empty placeholders | Empty inbox, no search results, missing permissions, failed list load |
141
+ | `ErrorBoundary` | React render error fallback | Unhandled screen crashes | Route-level fallback, feature boundary, recoverable widget crashes |
142
+ | `Icon` | Feather or custom icons with theme tokens | Raw vector icons with hardcoded colors | Button accessories, empty-state icons, menu icons, status glyphs |
143
+ | `InputOTP` | Verification code entry | Multiple manually managed text inputs | Email codes, SMS codes, MFA, invite codes |
144
+ | `Label` | Accessible form labels | Plain styled text labels | Required labels, disabled labels, field group labels |
145
+ | `MaxWidthContainer` | Centered responsive width | Per-screen max-width wrappers | Web pages, tablet layouts, settings forms, auth panels |
146
+ | `Notification` | Global toast surface | Screen-local toast state | Saved/error/sync notifications, loading toast, bottom-position alerts |
147
+ | `Popover` | Anchored contextual content | Custom anchored views | Inline help, quick previews, contextual controls, small forms |
148
+ | `Progress` | Determinate or indeterminate progress | Layout-shifting spinners for progress regions | Upload progress, onboarding completion, long-running task state |
149
+ | `RadioGroup`, `RadioGroupItem` | Mutually exclusive choices | Custom radio rows | Plan interval, visibility choice, survey answer, preference setting |
150
+ | `Select` | Option menus | Custom dropdowns | Country picker, category selector, status selector, compact form choice |
151
+ | `Separator` | Horizontal or vertical dividers | Border-only spacer views | Menu dividers, section dividers, card dividers |
152
+ | `Skeleton`, `SkeletonText`, `SkeletonAvatar`, `SkeletonCard` | Loading placeholders | Blank space or generic spinners | List loading, profile card loading, dashboard placeholders |
153
+ | `Slider` | Numeric value selection | Custom pan gesture track | Volume, percentage, rating, threshold, range-like settings |
154
+ | `StatusBar` | Theme-aware native status bar | Per-screen status-bar duplication | Root layout status styling, dark/light mode updates |
155
+ | `StyledText` and text aliases | Theme-aware typography | Raw `Text` with hardcoded styles | Titles, headings, labels, body copy, captions, translated text |
156
+ | `Switch` | Binary settings | Custom toggle switches | Enable notifications, privacy setting, feature toggles |
157
+ | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent` | In-page tabbed views | Custom segmented/tab controls | Profile sections, report views, settings categories |
158
+ | `TextInput` | Text entry | Raw `TextInput` with repeated label/error code | Email/password, search, numeric input, multiline notes |
159
+ | `Toggle`, `ToggleIcon` | Pressed/unpressed control | Button with local selected styling | Favorite, mute, bold/italic, view mode button |
160
+ | `ToggleGroup`, `ToggleGroupItem`, `ToggleGroupIcon` | Single or multi toggle groups | Custom segmented controls | Alignment, formatting toolbar, filter chips, view mode switcher |
161
+ | `Tooltip` | Short hover/focus help | Persistent helper text or custom hover cards | Icon button labels, field hints, disabled action explanations |
162
+
163
+ ### Compound Parts And Aliases
164
+
165
+ Most compound components support both direct named imports and dot notation on the root component. Prefer the named exports when code completion or LLM context benefits from explicit names.
166
+
167
+ | Root | Exported Parts |
168
+ |------|----------------|
169
+ | `Accordion` | `AccordionItem`, `AccordionTrigger`, `AccordionContent` |
170
+ | `AlertDialog` | `AlertDialogTrigger`, `AlertDialogContent`, `AlertDialogTitle`, `AlertDialogDescription`, `AlertDialogAction`, `AlertDialogCancel` |
171
+ | `BottomSheet` | `BottomSheetTrigger`, `BottomSheetContent`, `BottomSheetHandle`, `BottomSheetHeader`, `BottomSheetBody`, `BottomSheetFooter`, `BottomSheetClose` |
172
+ | `Card` | `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter` |
173
+ | `Collapsible` | `CollapsibleTrigger`, `CollapsibleContent` |
174
+ | `Dialog` | `DialogTrigger`, `DialogContent`, `DialogHeader`, `DialogFooter`, `DialogTitle`, `DialogDescription`, `DialogClose` |
175
+ | `Drawer` | `DrawerTrigger`, `DrawerContent`, `DrawerHeader`, `DrawerBody`, `DrawerFooter`, `DrawerClose` |
176
+ | `DropdownMenu` | `DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuGroup`, `DropdownMenuItem`, `DropdownMenuCheckboxItem`, `DropdownMenuRadioGroup`, `DropdownMenuRadioItem`, `DropdownMenuLabel`, `DropdownMenuSeparator`, `DropdownMenuShortcut`, `DropdownMenuPortal`, `DropdownMenuSub`, `DropdownMenuSubTrigger`, `DropdownMenuSubContent` |
177
+ | `Popover` | `PopoverTrigger`, `PopoverContent`, `PopoverHeader`, `PopoverBody`, `PopoverFooter` |
178
+ | `RadioGroup` | `RadioGroupItem` |
179
+ | `Select` | `SelectTrigger`, `SelectValue`, `SelectContent`, `SelectItem`, `SelectGroup`, `SelectLabel`, `SelectSeparator` |
180
+ | `Skeleton` | `SkeletonText`, `SkeletonAvatar`, `SkeletonCard` |
181
+ | `Tabs` | `TabsList`, `TabsTrigger`, `TabsContent` |
182
+ | `Toggle` | `ToggleIcon` |
183
+ | `ToggleGroup` | `ToggleGroupItem`, `ToggleGroupIcon` |
184
+ | `Tooltip` | `TooltipTrigger`, `TooltipContent`, `TooltipBody` |
185
+
186
+ Text aliases are exported for common semantic typography: `SerifText`, `SansSerifText`, `SerifBoldText`, `SansSerifBoldText`, `DisplayText`, `TitleText`, `HeadingText`, `SubheadingText`, `BodyText`, `CaptionText`, and `LabelText`. `TextClassContext` and `TextColorContext` are advanced context exports used by package controls to pass nested text styling.
187
+
188
+ ### Common Patterns
189
+
190
+ Use `Button.preset`, not `variant`. `default` is the neutral primary action, `secondary` is a neutral secondary surface, `outline` is for lower-emphasis actions, `ghost` is for compact toolbars, `link` is for text-like commands, and `destructive` is for dangerous actions.
191
+
192
+ Use `StyledText` or its aliases instead of raw `Text` whenever the text is part of app UI. Use `TextInput` for labeled fields because it already owns label, helper text, error text, clear buttons, password visibility, numeric filtering, and left/right elements.
193
+
194
+ Mount `UIProvider` once near the root before using `Dialog`, `AlertDialog`, `BottomSheet`, `Drawer`, `DropdownMenu`, `Popover`, `SelectContent`, `Tooltip`, or package notifications. Trigger transient feedback from `globalUIStore`.
195
+
196
+ 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.
197
+
198
+ Use `Toggle` for one pressed state, `ToggleGroup` for a related set of pressed states, `Switch` for binary settings, `RadioGroup` for small mutually exclusive choices, and `Select` for longer option sets.
199
+
200
+ ### Quick Examples
201
+
202
+ Card, badge, and CTA:
135
203
 
136
204
  ```tsx
137
205
  import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from "@mrmeg/expo-ui/components";
@@ -149,12 +217,49 @@ import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from "@mrmeg/
149
217
  </Card>
150
218
  ```
151
219
 
220
+ Form controls:
221
+
222
+ ```tsx
223
+ import { Button, TextInput, Switch } from "@mrmeg/expo-ui/components";
224
+
225
+ <TextInput
226
+ label="Email"
227
+ placeholder="you@example.com"
228
+ autoCapitalize="none"
229
+ keyboardType="email-address"
230
+ errorText={emailError}
231
+ />
232
+
233
+ <Switch checked={enabled} onCheckedChange={setEnabled} variant="ios" />
234
+
235
+ <Button preset="default" size="lg" fullWidth loading={isSubmitting}>
236
+ Continue
237
+ </Button>
238
+ ```
239
+
240
+ Feedback:
241
+
242
+ ```tsx
243
+ import { EmptyState, Progress, SkeletonCard } from "@mrmeg/expo-ui/components";
244
+ import { globalUIStore } from "@mrmeg/expo-ui/state";
245
+
246
+ {isLoading ? <SkeletonCard /> : null}
247
+ <Progress value={65} variant="accent" />
248
+ <EmptyState icon="inbox" title="No messages" description="New messages will appear here." />
249
+
250
+ globalUIStore.getState().show({
251
+ type: "success",
252
+ title: "Saved",
253
+ messages: ["Your changes were saved."],
254
+ });
255
+ ```
256
+
152
257
  LLM rules:
153
258
 
154
259
  - Prefer `StyledText` semantic variants over raw `Text`.
155
260
  - Use `useTheme()` and semantic tokens instead of hardcoded colors.
156
261
  - Use `Button.preset`, not `variant`, for buttons.
157
- - Mount `PortalHost` before using overlays, menus, select content, popovers, or tooltips.
262
+ - Mount `UIProvider` before using overlays, menus, select content, popovers, tooltips, or package notifications.
158
263
  - Import from package exports, not `packages/ui/src/*`.
159
264
 
160
265
  ## App Startup
@@ -165,8 +270,7 @@ Call `useResources()` once near the Expo app root before hiding the splash scree
165
270
  import { ThemeProvider } from "@react-navigation/native";
166
271
  import { colors } from "@mrmeg/expo-ui/constants";
167
272
  import { useResources, useTheme } from "@mrmeg/expo-ui/hooks";
168
- import { Notification, StatusBar } from "@mrmeg/expo-ui/components";
169
- import { PortalHost } from "@rn-primitives/portal";
273
+ import { UIProvider } from "@mrmeg/expo-ui/components";
170
274
 
171
275
  export default function RootLayout() {
172
276
  const { scheme } = useTheme();
@@ -182,10 +286,9 @@ export default function RootLayout() {
182
286
  fonts: colors[scheme ?? "light"].fonts,
183
287
  }}
184
288
  >
185
- {/* App navigation goes here. */}
186
- <Notification />
187
- <PortalHost />
188
- <StatusBar />
289
+ <UIProvider>
290
+ {/* App navigation goes here. */}
291
+ </UIProvider>
189
292
  </ThemeProvider>
190
293
  );
191
294
  }
@@ -211,7 +314,50 @@ On native, the package uses platform sans-serif fallbacks. `useResources()` stil
211
314
 
212
315
  ## Package Checks
213
316
 
214
- Run these before publishing:
317
+ For a one-command release from the repo root, use:
318
+
319
+ ```sh
320
+ bun run ui:release -- --patch --publish
321
+ ```
322
+
323
+ Replace `--patch` with `--minor`, `--major`, or an exact version such as `0.2.0`.
324
+ The command updates `packages/ui/package.json` and `bun.lock`, runs all package
325
+ gates, then publishes with `npm publish --access public` only when `--publish`
326
+ is present. Without `--publish`, it performs the same version bump and gates as
327
+ a dry run.
328
+
329
+ The release command requires a clean working tree by default. Commit current
330
+ changes first, or pass `--allow-dirty` when you intentionally want to release
331
+ from uncommitted local changes.
332
+
333
+ If npm login email is unavailable, publish through GitHub Actions trusted
334
+ publishing instead:
335
+
336
+ 1. In npm package settings for `@mrmeg/expo-ui`, add a trusted publisher:
337
+ GitHub Actions, owner `mrmeg`, repository `expo-template`, workflow
338
+ filename `publish-ui.yml`.
339
+ 2. In GitHub Actions, run the `Publish UI Package` workflow with `version=patch`
340
+ and `ref=dev`.
341
+
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`.
346
+
347
+ Keep `repository.url` in `package.json` as
348
+ `git+https://github.com/mrmeg/expo-template.git`. npm trusted publishing checks
349
+ that metadata during publish, and publish-time URL normalization can block OIDC
350
+ auth.
351
+
352
+ If npm trusted publishing is blocked by package settings, add an npm automation
353
+ or granular publish token to GitHub Actions secrets as `NPM_TOKEN` and rerun the
354
+ same workflow. The token is used only for the publish step.
355
+
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.
359
+
360
+ Manual package checks:
215
361
 
216
362
  ```sh
217
363
  bun run ui:typecheck
@@ -224,5 +370,6 @@ bun run ui:consumer-smoke
224
370
  `bun run ui:pack` runs a dry pack so the published file list and package size can be inspected before release.
225
371
  `bun run ui:consumer-smoke` installs the packed tarball into a clean fixture,
226
372
  checks the documented export-map files, type-checks all public package
227
- entrypoints, and verifies that `@mrmeg/expo-ui/constants` can be imported
228
- by plain Node ESM for token inspection.
373
+ entrypoints, verifies that `@mrmeg/expo-ui/constants` can be imported by
374
+ plain Node ESM for token inspection, and runs an Expo SDK 55 iOS export
375
+ against the packed package without a custom Metro config.
@@ -0,0 +1,23 @@
1
+ import * as React from "react";
2
+ export interface UIProviderProps {
3
+ children: React.ReactNode;
4
+ /**
5
+ * Mount the package notification renderer for globalUIStore feedback.
6
+ *
7
+ * @default true
8
+ */
9
+ notification?: boolean;
10
+ /**
11
+ * Mount the default @rn-primitives portal host used by package overlays.
12
+ *
13
+ * @default true
14
+ */
15
+ portalHost?: boolean;
16
+ /**
17
+ * Mount the package status bar renderer.
18
+ *
19
+ * @default true
20
+ */
21
+ statusBar?: boolean;
22
+ }
23
+ export declare function UIProvider({ children, notification, portalHost, statusBar, }: UIProviderProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PortalHost } from "@rn-primitives/portal";
3
+ import { Notification } from "./Notification.js";
4
+ import { StatusBar } from "./StatusBar.js";
5
+ export function UIProvider({ children, notification = true, portalHost = true, statusBar = true, }) {
6
+ return (_jsxs(_Fragment, { children: [children, notification ? _jsx(Notification, {}) : null, portalHost ? _jsx(PortalHost, {}) : null, statusBar ? _jsx(StatusBar, {}) : null] }));
7
+ }
@@ -33,3 +33,4 @@ export * from "./TextInput";
33
33
  export * from "./Toggle";
34
34
  export * from "./ToggleGroup";
35
35
  export * from "./Tooltip";
36
+ export * from "./UIProvider";
@@ -33,3 +33,4 @@ export * from "./TextInput.js";
33
33
  export * from "./Toggle.js";
34
34
  export * from "./ToggleGroup.js";
35
35
  export * from "./Tooltip.js";
36
+ export * from "./UIProvider.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrmeg/expo-ui",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "description": "Reusable Expo and React Native UI primitives for MrMeg projects.",
6
6
  "keywords": [
@@ -31,7 +31,8 @@
31
31
  "files": [
32
32
  "dist",
33
33
  "package.json",
34
- "README.md"
34
+ "README.md",
35
+ "LLM_USAGE.md"
35
36
  ],
36
37
  "exports": {
37
38
  ".": {
@@ -50,10 +51,18 @@
50
51
  "types": "./dist/constants/index.d.ts",
51
52
  "default": "./dist/constants/index.js"
52
53
  },
54
+ "./constants/*": {
55
+ "types": "./dist/constants/*.d.ts",
56
+ "default": "./dist/constants/*.js"
57
+ },
53
58
  "./hooks": {
54
59
  "types": "./dist/hooks/index.d.ts",
55
60
  "default": "./dist/hooks/index.js"
56
61
  },
62
+ "./hooks/*": {
63
+ "types": "./dist/hooks/*.d.ts",
64
+ "default": "./dist/hooks/*.js"
65
+ },
57
66
  "./state": {
58
67
  "types": "./dist/state/index.d.ts",
59
68
  "default": "./dist/state/index.js"
@@ -69,6 +78,9 @@
69
78
  "build": "rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/fix-ui-package-esm.mjs",
70
79
  "publish:dry-run": "bun pm pack --dry-run"
71
80
  },
81
+ "dependencies": {
82
+ "@rn-primitives/portal": "~1.4.0"
83
+ },
72
84
  "peerDependencies": {
73
85
  "@expo/vector-icons": ">=15.0.0 <16.0.0",
74
86
  "@react-native-async-storage/async-storage": ">=2.2.0 <2.3.0",
@@ -80,7 +92,6 @@
80
92
  "@rn-primitives/dropdown-menu": "~1.4.0",
81
93
  "@rn-primitives/label": "~1.4.0",
82
94
  "@rn-primitives/popover": "~1.4.0",
83
- "@rn-primitives/portal": "~1.4.0",
84
95
  "@rn-primitives/radio-group": "~1.4.0",
85
96
  "@rn-primitives/select": "~1.4.0",
86
97
  "@rn-primitives/separator": "~1.4.0",
@@ -94,11 +105,14 @@
94
105
  "expo": "~55.0.0",
95
106
  "expo-font": "~55.0.0",
96
107
  "expo-haptics": "~55.0.0",
108
+ "i18next": ">=25.0.0 <27.0.0",
97
109
  "react": ">=19.2.0 <20.0.0",
110
+ "react-i18next": ">=15.0.0 <18.0.0",
98
111
  "react-native": ">=0.83.0 <0.84.0",
99
112
  "react-native-gesture-handler": "~2.30.0",
100
113
  "react-native-reanimated": "~4.2.0",
101
114
  "react-native-safe-area-context": "~5.6.0",
115
+ "react-native-screens": "~4.23.0",
102
116
  "react-native-web": ">=0.21.0 <0.22.0",
103
117
  "react-native-worklets": "~0.7.0",
104
118
  "zustand": ">=5.0.0 <6.0.0"