@mrmeg/expo-ui 0.23.0 → 0.24.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 +58 -0
- package/LLM_USAGE.md +159 -157
- package/README.md +421 -352
- package/dist/components/BottomSheet.js +1 -1
- package/dist/components/DismissKeyboard.d.ts +14 -3
- package/dist/components/DismissKeyboard.js +17 -48
- package/dist/components/InputOTP.js +4 -1
- package/dist/components/TextInput.js +189 -42
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/keyboardDismiss.d.ts +37 -0
- package/dist/components/keyboardDismiss.js +170 -0
- package/dist/components/keyboardFocusRegistry.d.ts +10 -14
- package/dist/components/keyboardFocusRegistry.js +8 -24
- package/llms-full.md +134 -124
- package/llms.txt +24 -22
- package/package.json +1 -1
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Registry of the currently focused native (`@expo/ui`) text field.
|
|
3
|
+
*
|
|
4
|
+
* React Native's `TextInputState` never learns about SwiftUI / Compose fields,
|
|
5
|
+
* so `Keyboard.dismiss()` cannot blur them. The package `TextInput` registers a
|
|
6
|
+
* blur handle here on focus; tap-away dismissal (`keyboardDismiss.ts`) and the
|
|
7
|
+
* `BottomSheet` overlay resign the field through it.
|
|
8
|
+
*/
|
|
4
9
|
export type KeyboardFocusedInputToken = object;
|
|
5
10
|
/**
|
|
6
11
|
* Subscribe to focus presence changes. Pairs with {@link hasKeyboardFocusedInput}
|
|
@@ -9,16 +14,8 @@ export type KeyboardFocusedInputToken = object;
|
|
|
9
14
|
* keyboards raised inside a native sheet's isolated window.
|
|
10
15
|
*/
|
|
11
16
|
export declare function subscribeKeyboardFocus(listener: () => void): () => void;
|
|
12
|
-
export declare function
|
|
13
|
-
export declare function
|
|
14
|
-
export declare function getKeyboardFocusedInputLayout(): {
|
|
15
|
-
x: number;
|
|
16
|
-
y: number;
|
|
17
|
-
width: number;
|
|
18
|
-
height: number;
|
|
19
|
-
absoluteX: number;
|
|
20
|
-
absoluteY: number;
|
|
21
|
-
} | undefined;
|
|
17
|
+
export declare function setKeyboardFocusedInput(token: KeyboardFocusedInputToken, blur: () => void): void;
|
|
18
|
+
export declare function clearKeyboardFocusedInput(token: KeyboardFocusedInputToken): void;
|
|
22
19
|
/** Whether any native field currently holds focus. Stable boolean for snapshots. */
|
|
23
20
|
export declare function hasKeyboardFocusedInput(): boolean;
|
|
24
21
|
/**
|
|
@@ -26,4 +23,3 @@ export declare function hasKeyboardFocusedInput(): boolean;
|
|
|
26
23
|
* `true` if a field was focused and a blur handle was available.
|
|
27
24
|
*/
|
|
28
25
|
export declare function dismissKeyboardFocusedInput(): boolean;
|
|
29
|
-
export declare function isTouchInsideKeyboardFocusedInput(event: GestureResponderEvent, nativeLayout?: KeyboardFocusedInputLayout | null): boolean;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const FOCUSED_INPUT_TOUCH_SLOP = 16;
|
|
2
1
|
let focusedInput = null;
|
|
3
2
|
const listeners = new Set();
|
|
4
3
|
function emit() {
|
|
@@ -16,23 +15,22 @@ export function subscribeKeyboardFocus(listener) {
|
|
|
16
15
|
listeners.delete(listener);
|
|
17
16
|
};
|
|
18
17
|
}
|
|
19
|
-
export function
|
|
18
|
+
export function setKeyboardFocusedInput(token, blur) {
|
|
20
19
|
const wasPresent = focusedInput != null;
|
|
21
|
-
focusedInput = { token,
|
|
22
|
-
// Only notify subscribers on a presence transition;
|
|
23
|
-
//
|
|
20
|
+
focusedInput = { token, blur };
|
|
21
|
+
// Only notify subscribers on a presence transition; a focus handoff between
|
|
22
|
+
// two fields keeps presence true and stays silent.
|
|
24
23
|
if (!wasPresent)
|
|
25
24
|
emit();
|
|
26
25
|
}
|
|
27
|
-
export function
|
|
26
|
+
export function clearKeyboardFocusedInput(token) {
|
|
27
|
+
// Token-guarded: when focus hops A -> B, B's focus can land before A's blur,
|
|
28
|
+
// and A's late clear must not wipe B's registration.
|
|
28
29
|
if (focusedInput?.token === token) {
|
|
29
30
|
focusedInput = null;
|
|
30
31
|
emit();
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
|
-
export function getKeyboardFocusedInputLayout() {
|
|
34
|
-
return focusedInput?.layout;
|
|
35
|
-
}
|
|
36
34
|
/** Whether any native field currently holds focus. Stable boolean for snapshots. */
|
|
37
35
|
export function hasKeyboardFocusedInput() {
|
|
38
36
|
return focusedInput != null;
|
|
@@ -42,23 +40,9 @@ export function hasKeyboardFocusedInput() {
|
|
|
42
40
|
* `true` if a field was focused and a blur handle was available.
|
|
43
41
|
*/
|
|
44
42
|
export function dismissKeyboardFocusedInput() {
|
|
45
|
-
if (focusedInput
|
|
43
|
+
if (focusedInput) {
|
|
46
44
|
focusedInput.blur();
|
|
47
45
|
return true;
|
|
48
46
|
}
|
|
49
47
|
return false;
|
|
50
48
|
}
|
|
51
|
-
function isTouchInsideLayout(event, layout) {
|
|
52
|
-
if (!layout)
|
|
53
|
-
return false;
|
|
54
|
-
const { pageX, pageY } = event.nativeEvent;
|
|
55
|
-
const left = layout.absoluteX - FOCUSED_INPUT_TOUCH_SLOP;
|
|
56
|
-
const right = layout.absoluteX + layout.width + FOCUSED_INPUT_TOUCH_SLOP;
|
|
57
|
-
const top = layout.absoluteY - FOCUSED_INPUT_TOUCH_SLOP;
|
|
58
|
-
const bottom = layout.absoluteY + layout.height + FOCUSED_INPUT_TOUCH_SLOP;
|
|
59
|
-
return pageX >= left && pageX <= right && pageY >= top && pageY <= bottom;
|
|
60
|
-
}
|
|
61
|
-
export function isTouchInsideKeyboardFocusedInput(event, nativeLayout) {
|
|
62
|
-
return (isTouchInsideLayout(event, getKeyboardFocusedInputLayout()) ||
|
|
63
|
-
isTouchInsideLayout(event, nativeLayout));
|
|
64
|
-
}
|
package/llms-full.md
CHANGED
|
@@ -1,154 +1,172 @@
|
|
|
1
1
|
# @mrmeg/expo-ui Full Contract
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
media, monitoring, API calls, product copy, and app-specific layouts.
|
|
3
|
+
The package owns the shared design system, tokens, theme store, resource hook,
|
|
4
|
+
global notification store, overlay shell, and small interaction helpers. The
|
|
5
|
+
consuming app owns routes, screens, feature state, auth, billing, media,
|
|
6
|
+
monitoring, API calls, product copy, and app-specific layouts.
|
|
8
7
|
|
|
9
8
|
## Agent Rules
|
|
10
9
|
|
|
11
|
-
Do not recreate primitives
|
|
10
|
+
Do not recreate primitives this package already provides. Import from
|
|
12
11
|
`@mrmeg/expo-ui` and compose package components in the app.
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
Importable paths, and nothing else:
|
|
15
14
|
|
|
16
15
|
- `@mrmeg/expo-ui`
|
|
17
|
-
- `@mrmeg/expo-ui/components
|
|
18
|
-
- `@mrmeg/expo-ui/
|
|
19
|
-
- `@mrmeg/expo-ui/
|
|
20
|
-
- `@mrmeg/expo-ui/
|
|
21
|
-
- `@mrmeg/expo-ui/hooks`
|
|
22
|
-
- `@mrmeg/expo-ui/hooks/*`
|
|
23
|
-
- `@mrmeg/expo-ui/state`
|
|
16
|
+
- `@mrmeg/expo-ui/components`, `@mrmeg/expo-ui/components/*`
|
|
17
|
+
- `@mrmeg/expo-ui/constants`, `@mrmeg/expo-ui/constants/*`
|
|
18
|
+
- `@mrmeg/expo-ui/hooks`, `@mrmeg/expo-ui/hooks/*`
|
|
19
|
+
- `@mrmeg/expo-ui/state`, `@mrmeg/expo-ui/state/*`
|
|
24
20
|
- `@mrmeg/expo-ui/lib`
|
|
25
21
|
|
|
26
|
-
|
|
22
|
+
Never import from `@mrmeg/expo-ui/dist/*`, `packages/ui/src/*`, or copied
|
|
27
23
|
app-local component files.
|
|
28
24
|
|
|
25
|
+
```tsx
|
|
26
|
+
import { Button, StyledText, UIProvider } from "@mrmeg/expo-ui/components";
|
|
27
|
+
import { Button as ButtonDirect } from "@mrmeg/expo-ui/components/Button";
|
|
28
|
+
import { colors, spacing, typography } from "@mrmeg/expo-ui/constants";
|
|
29
|
+
import { useResources, useStyles, useTheme } from "@mrmeg/expo-ui/hooks";
|
|
30
|
+
import { globalUIStore, notify, ThemeColorScope, useThemeStore } from "@mrmeg/expo-ui/state";
|
|
31
|
+
import { configureExpoUiI18n, hapticLight } from "@mrmeg/expo-ui/lib";
|
|
32
|
+
// The root barrel re-exports the whole public surface:
|
|
33
|
+
import { Button, colors, UIProvider, useTheme } from "@mrmeg/expo-ui";
|
|
34
|
+
```
|
|
35
|
+
|
|
29
36
|
## App Setup
|
|
30
37
|
|
|
31
|
-
Call `useResources()` once near the Expo app root
|
|
32
|
-
the
|
|
38
|
+
Call `useResources()` once near the Expo app root; it resolves `{ loaded, error }`
|
|
39
|
+
and loads the Feather icon font plus the four static Inter weights (web gets a
|
|
40
|
+
single Google Fonts Inter stylesheet).
|
|
33
41
|
|
|
34
|
-
`UIProvider` owns the package `Notification`,
|
|
35
|
-
`@rn-primitives` portal host, and native keyboard-avoiding
|
|
36
|
-
before
|
|
37
|
-
`Popover`, `SelectContent`, `Tooltip`, or `notify` /
|
|
38
|
-
|
|
39
|
-
keyboard avoidance, or use `KeyboardAvoidingView` directly for a subtree with
|
|
40
|
-
custom behavior. Web skips the root keyboard wrapper unless `keyboardAvoiding`
|
|
41
|
-
is explicitly enabled.
|
|
42
|
+
Mount `UIProvider` once near the root. It owns the package `Notification`,
|
|
43
|
+
`StatusBar`, default `@rn-primitives` portal host, and native keyboard-avoiding
|
|
44
|
+
root, and is required before `Dialog`, `AlertDialog`, `BottomSheet`, `Drawer`,
|
|
45
|
+
`DropdownMenu`, `Popover`, `SelectContent`, `Tooltip`, or `notify` /
|
|
46
|
+
`globalUIStore` notifications.
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
`UIProvider` props: `notification`, `portalHost`, `statusBar` (default `true`),
|
|
49
|
+
`keyboardAvoiding` (default `true` on native, `false` on web), and
|
|
50
|
+
`keyboardAvoidingProps` forwarded to the root wrapper. Native keyboard avoidance
|
|
51
|
+
uses `react-native-keyboard-controller`, so mount its `KeyboardProvider` above
|
|
52
|
+
`UIProvider`; use `KeyboardAvoidingView` directly for a subtree needing custom
|
|
53
|
+
behavior.
|
|
46
54
|
|
|
47
55
|
i18n is optional. Plain children and `text` props work without `i18next` or
|
|
48
|
-
`react-i18next`. Use `configureExpoUiI18n()` only when
|
|
49
|
-
|
|
56
|
+
`react-i18next`. Use `configureExpoUiI18n()` only when the app wants package
|
|
57
|
+
`tx` props translated by its own i18n instance.
|
|
50
58
|
|
|
51
59
|
## Theme Rules
|
|
52
60
|
|
|
53
|
-
Use `useTheme()` and `useStyles()` from `@mrmeg/expo-ui/hooks`.
|
|
54
|
-
|
|
55
|
-
`
|
|
56
|
-
`mutedForeground`, `
|
|
61
|
+
Use `useTheme()` and `useStyles()` from `@mrmeg/expo-ui/hooks`. Semantic tokens
|
|
62
|
+
on `theme.colors`: `surfaceSunken`, `background`, `foreground`, `card`,
|
|
63
|
+
`cardForeground`, `popover`, `popoverForeground`, `text`, `textDim`, `muted`,
|
|
64
|
+
`mutedForeground`, `border`, `borderStrong`, `input`, `ring`, `overlay`,
|
|
65
|
+
`primary`, `secondary`, `accent`, their `*Foreground` pairs, `destructive`,
|
|
66
|
+
`success`, `warning`.
|
|
57
67
|
|
|
58
68
|
Surfaces form a tier ladder rather than a shadow scale: `surfaceSunken` (app
|
|
59
69
|
chrome such as the Drawer rail) < `background` (content) < `card`/`popover`
|
|
60
|
-
(raised) < `muted` (chips, insets). Use `borderStrong` for hairlines on filled
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
Use `StyledText` and semantic text aliases instead of raw `Text` for app UI.
|
|
64
|
-
Use package controls instead of hardcoded Pressable/View/Text combinations.
|
|
65
|
-
|
|
66
|
-
When the saved theme preference is `system`, the package theme store owns OS
|
|
67
|
-
color-scheme sync, including web `prefers-color-scheme`. Apps should not add
|
|
68
|
-
their own `Appearance` or `matchMedia` listeners just to make package
|
|
69
|
-
components follow system light/dark changes.
|
|
70
|
+
(raised) < `muted` (chips, insets). Use `borderStrong` for hairlines on filled or
|
|
71
|
+
raised elements, where `border` would blend into the fill.
|
|
70
72
|
|
|
71
|
-
|
|
73
|
+
On web every theme color resolves to a CSS custom property (`var(--c-*)`) so the
|
|
74
|
+
app re-themes in CSS when `html[data-theme]` changes; native keeps literals.
|
|
75
|
+
Consequence: hex-suffix alpha (`theme.colors.x + "15"`) does not work — use
|
|
76
|
+
`withAlpha(color, alpha)`. `constants` also exports `rawThemeColors.light/.dark`
|
|
77
|
+
and `resolveRawColor(color, scheme)` for sinks that cannot take `var()`, and
|
|
78
|
+
`getThemeCssVariables(overrides?)` for an app's `+html.tsx`.
|
|
72
79
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
import { globalUIStore, notify, useThemeStore } from "@mrmeg/expo-ui/state";
|
|
79
|
-
import { configureExpoUiI18n, hapticLight } from "@mrmeg/expo-ui/lib";
|
|
80
|
-
```
|
|
80
|
+
Color overrides resolve in three layers, last wins: package defaults → global
|
|
81
|
+
brand (`useThemeStore.getState().setColors({ light?, dark? })`) → subtree scope
|
|
82
|
+
(`<ThemeColorScope colors={{ light, dark }}>`). Each is a `Partial<ThemeColors>`.
|
|
83
|
+
`setFonts` and `setShape({ button: { borderRadius?, withShadow? } })` are the
|
|
84
|
+
font and geometry counterparts; per-instance props always win.
|
|
81
85
|
|
|
82
|
-
|
|
86
|
+
Use `StyledText` and semantic text aliases instead of raw `Text`, and package
|
|
87
|
+
controls instead of hardcoded Pressable/View/Text combinations.
|
|
83
88
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
When the saved theme preference is `system`, the package theme store owns OS
|
|
90
|
+
color-scheme sync, including web `prefers-color-scheme`. Apps must not add their
|
|
91
|
+
own `Appearance` or `matchMedia` listeners for package components.
|
|
87
92
|
|
|
88
93
|
## Component Catalog
|
|
89
94
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
|
95
|
-
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
113
|
-
| `
|
|
114
|
-
| `
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
95
|
+
Every component is exported from `@mrmeg/expo-ui/components`, and from
|
|
96
|
+
`@mrmeg/expo-ui/components/<Name>` for a direct import. Check this catalog
|
|
97
|
+
before creating a new app-local primitive.
|
|
98
|
+
|
|
99
|
+
| Component | Use When | Gotchas |
|
|
100
|
+
|-----------|----------|---------|
|
|
101
|
+
| `Accordion` | Multi-section disclosure such as FAQ or grouped settings | Use the compound parts instead of custom expanders. |
|
|
102
|
+
| `Alert` | Cross-platform imperative alerts | Avoid `window.alert` and duplicated native/web branching. |
|
|
103
|
+
| `AnimatedView` | Entrance and visibility animation | Keep simple reveal effects in this wrapper. |
|
|
104
|
+
| `Avatar`, `AvatarGroup` | Profile images with initials/icon fallback | Pass both `source` and `name`; set `size`/`shape` on the group, not per child. |
|
|
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. |
|
|
107
|
+
| `Button` | Commands and CTAs | Use `preset`, not `variant`; heights are 28/32/40. |
|
|
108
|
+
| `Card` | Individual framed content groups | Do not wrap whole page sections in cards. |
|
|
109
|
+
| `Carousel` | Small known set of horizontally snapping slides | Renders every child (no virtualization); fractional `itemWidth` measures the viewport until first layout. |
|
|
110
|
+
| `Checkbox` | Boolean selection in forms or lists | Prefer over custom checkmark controls. |
|
|
111
|
+
| `Collapsible` | One-off disclosure | Use for advanced settings or helper sections. |
|
|
112
|
+
| `Dialog`, `AlertDialog` | Blocking modal content or decisions | Requires root `UIProvider` portal host. |
|
|
113
|
+
| `DismissKeyboard` | Tap-away keyboard dismissal | Prefer over screen-level keyboard wrappers. |
|
|
114
|
+
| `Drawer` | Side panels and drawer navigation | `Drawer.Content` owns safe-area top/bottom padding; do not duplicate it in children. |
|
|
115
|
+
| `DropdownMenu` | Menus and command lists | Requires root `UIProvider` portal host. |
|
|
116
|
+
| `EmptyState` | No-data or recoverable error regions | Props: `icon`, `title`, `description`, `actionLabel`, `onAction`, `actionPreset`. |
|
|
117
|
+
| `ErrorBoundary` | React render error fallback | Use for route or feature boundaries. |
|
|
118
|
+
| `Icon` | Feather or custom icons with theme tokens | `color` takes a theme color name; pass `decorative` to hide from a11y. |
|
|
119
|
+
| `InputOTP` | Verification code entry | Prefer over manually managed text input groups. |
|
|
120
|
+
| `Item` | List / settings rows | Applies the row density tokens and a 44px native hit area (40px on web). |
|
|
121
|
+
| `KeyboardAvoidingView` | Native keyboard-aware roots, composer footers, form-heavy subtrees | `UIProvider` already mounts one root; use directly only for custom subtrees. |
|
|
122
|
+
| `Label` | Accessible form labels | Two distinct ids: `nativeID` is the label's, `htmlFor` is the input's `nativeID`. Never reuse one id for both. |
|
|
123
|
+
| `MaxWidthContainer` | Centered responsive width | Use for web and tablet constrained layouts. |
|
|
124
|
+
| `Notification` | Global toast surface | Trigger through `notify` (or `globalUIStore` for subscriptions/tests) with root `UIProvider`; actions dismiss after press. |
|
|
125
|
+
| `Popover` | Anchored contextual content | Requires root `UIProvider` portal host. |
|
|
126
|
+
| `Progress` | Determinate or indeterminate progress | Omit `value` for indeterminate. Prefer over layout-shifting spinners. |
|
|
127
|
+
| `RadioGroup` | Small mutually exclusive choices | Use `Select` for longer option sets. |
|
|
128
|
+
| `SectionHeader` | Eyebrow / title / description section intro | Prefer over stacked ad hoc heading text. |
|
|
129
|
+
| `SegmentedControl` | Native segmented picker | Backed by `@expo/ui`; the platform owns its look. |
|
|
130
|
+
| `Select` | Option menus | `SelectContent` requires root `UIProvider` portal host; `label` drives default item text. |
|
|
131
|
+
| `Separator` | Horizontal or vertical dividers | Prefer over border-only spacer views. |
|
|
132
|
+
| `Skeleton` | Loading placeholders | Use stable dimensions to avoid layout shift. |
|
|
133
|
+
| `Slider` | Numeric value selection | Backed by `@expo/ui`; prefer over custom pan gesture tracks. |
|
|
134
|
+
| `StatCard` | Metric tile | Props: `label`, `value`, `unit`, `change` (`{ value, direction }`), `icon`, `onPress`. |
|
|
135
|
+
| `StatusBar` | Theme-aware native status bar | Usually mounted through `UIProvider`. |
|
|
136
|
+
| `StyledText` | Theme-aware typography | Prefer semantic aliases over raw `Text`. |
|
|
137
|
+
| `Switch` | Binary settings | Prefer over custom toggles for on/off state. |
|
|
138
|
+
| `Tabs` | In-page tabbed views | Sizes are `sm`/`md` only. |
|
|
139
|
+
| `TextInput` | Text entry | Owns label, helper/error text, clear button, password reveal, numeric filtering, left/right elements. |
|
|
140
|
+
| `Toggle` | One pressed/unpressed control | Sizes are `sm`/`default`/`lg`. Use `Button` for commands, `Switch` for settings. |
|
|
141
|
+
| `ToggleGroup` | Related pressed states | Use for segmented controls, formatting, and filter chips. |
|
|
142
|
+
| `Tooltip` | Short hover/focus help | Requires root `UIProvider` portal host. |
|
|
143
|
+
|
|
144
|
+
Every compound part has a named export (`CardHeader`, `AccordionItem`,
|
|
145
|
+
`SelectContent`, …). Dot notation exists only on `AlertDialog`, `BottomSheet`,
|
|
146
|
+
`Button`, `Dialog`, `Drawer`, `Popover`, `RadioGroup`, `Select`, `Tabs`, and
|
|
147
|
+
`Tooltip` — `Card`, `Accordion`, `Collapsible`, `DropdownMenu`, `Item`,
|
|
148
|
+
`Skeleton`, `Toggle`, and `ToggleGroup` are named exports only.
|
|
130
149
|
|
|
131
150
|
## Selection Rules
|
|
132
151
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
Use `RadioGroup` for small mutually exclusive choices and `Select` for longer
|
|
152
|
+
`Button` commands · `Toggle` one pressed state · `ToggleGroup` a related set ·
|
|
153
|
+
`Switch` binary settings · `RadioGroup` few exclusive choices · `Select` longer
|
|
137
154
|
option sets.
|
|
138
155
|
|
|
139
|
-
|
|
140
|
-
|
|
156
|
+
`Dialog` blocking decisions · `Popover` contextual controls · `Tooltip` short
|
|
157
|
+
explanations · `DropdownMenu` action lists.
|
|
141
158
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
progress or indeterminate long-running work.
|
|
159
|
+
`Card` individual repeated or framed items, never a wrapper around full page
|
|
160
|
+
sections · `EmptyState` no-data or recoverable errors · `Skeleton` loading
|
|
161
|
+
content with stable layout · `Progress` real or indeterminate progress.
|
|
146
162
|
|
|
147
163
|
## Notifications
|
|
148
164
|
|
|
149
|
-
`notify`
|
|
150
|
-
|
|
151
|
-
|
|
165
|
+
`notify` (from `@mrmeg/expo-ui/state`, also on the root barrel) is the imperative
|
|
166
|
+
API for the `Notification` component. Notifications auto-dismiss after 4s
|
|
167
|
+
(`DEFAULT_NOTIFICATION_DURATION`) unless a `duration` is given; `duration: 0`
|
|
168
|
+
keeps one up until dismissed, and `notify.loading` never auto-dismisses.
|
|
169
|
+
`position` is `"top"` (default) or `"bottom"`.
|
|
152
170
|
|
|
153
171
|
```ts
|
|
154
172
|
import { notify } from "@mrmeg/expo-ui/state";
|
|
@@ -158,7 +176,6 @@ notify.error("Upload failed");
|
|
|
158
176
|
notify.warning("Connection slow");
|
|
159
177
|
notify.info("Copied to clipboard");
|
|
160
178
|
|
|
161
|
-
// Loading spinner — persists until replaced or hidden (no auto-dismiss)
|
|
162
179
|
notify.loading("Uploading…");
|
|
163
180
|
notify.hide();
|
|
164
181
|
|
|
@@ -173,19 +190,12 @@ await notify.promise(saveProfile(), {
|
|
|
173
190
|
});
|
|
174
191
|
```
|
|
175
192
|
|
|
176
|
-
`globalUIStore` (the underlying zustand store)
|
|
193
|
+
`globalUIStore` (the underlying zustand store) stays available for reactive
|
|
194
|
+
selectors and tests. Use `notify` for imperative triggers in app code.
|
|
177
195
|
|
|
178
196
|
## Validation
|
|
179
197
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
bun run ui:test
|
|
185
|
-
bun run ui:build
|
|
186
|
-
bun run ui:pack
|
|
187
|
-
bun run ui:consumer-smoke
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
For documentation-only package surface changes, `bun run ui:pack` is the
|
|
191
|
-
minimum check that proves the new docs are included in the npm tarball.
|
|
198
|
+
Changing package code or shipped docs? Run the gates from the monorepo root:
|
|
199
|
+
`ui:typecheck`, `ui:test`, `ui:build`, `ui:pack`, `ui:consumer-smoke`
|
|
200
|
+
(`bun run <gate>`). Docs-only changes need at least `ui:pack`, which proves the
|
|
201
|
+
new docs land in the npm tarball.
|
package/llms.txt
CHANGED
|
@@ -1,36 +1,38 @@
|
|
|
1
1
|
# @mrmeg/expo-ui
|
|
2
2
|
|
|
3
|
-
Docs
|
|
3
|
+
Docs, in `node_modules/@mrmeg/expo-ui/`:
|
|
4
4
|
|
|
5
|
-
- README.md
|
|
6
|
-
- LLM_USAGE.md
|
|
7
|
-
- llms-full.md
|
|
5
|
+
- README.md — install, setup, theme, component guide, release.
|
|
6
|
+
- LLM_USAGE.md — concise component-selection and implementation rules.
|
|
7
|
+
- llms-full.md — expanded contract, component catalog, notifications.
|
|
8
8
|
|
|
9
|
-
Public entrypoints:
|
|
9
|
+
Public entrypoints (nothing else is importable):
|
|
10
10
|
|
|
11
11
|
- @mrmeg/expo-ui
|
|
12
|
-
- @mrmeg/expo-ui/components
|
|
13
|
-
- @mrmeg/expo-ui/
|
|
14
|
-
- @mrmeg/expo-ui/
|
|
15
|
-
- @mrmeg/expo-ui/
|
|
16
|
-
- @mrmeg/expo-ui/hooks
|
|
17
|
-
- @mrmeg/expo-ui/hooks/*
|
|
18
|
-
- @mrmeg/expo-ui/state
|
|
12
|
+
- @mrmeg/expo-ui/components, /components/*
|
|
13
|
+
- @mrmeg/expo-ui/constants, /constants/*
|
|
14
|
+
- @mrmeg/expo-ui/hooks, /hooks/*
|
|
15
|
+
- @mrmeg/expo-ui/state, /state/*
|
|
19
16
|
- @mrmeg/expo-ui/lib
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
Never import from /dist/*, a source checkout, or an app-local copy.
|
|
19
|
+
|
|
20
|
+
Do not recreate primitives this package provides; compose the exports.
|
|
23
21
|
|
|
24
22
|
Call useResources() once near the Expo app root. Mount UIProvider once near the
|
|
25
|
-
root
|
|
26
|
-
|
|
27
|
-
Tooltip.
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
root: it owns Notification, StatusBar, the @rn-primitives portal host, and the
|
|
24
|
+
native keyboard-avoiding root. Required before Dialog, AlertDialog, BottomSheet,
|
|
25
|
+
Drawer, DropdownMenu, Popover, SelectContent, Tooltip, and notify. On native,
|
|
26
|
+
mount react-native-keyboard-controller's KeyboardProvider above UIProvider.
|
|
27
|
+
|
|
28
|
+
BottomSheet renders the platform's native sheet via @expo/ui, which owns
|
|
29
|
+
gestures and keyboard avoidance; swipeEnabled, avoidKeyboard, and
|
|
30
|
+
dismissKeyboardOnDrag are accepted but ignored.
|
|
30
31
|
|
|
31
32
|
Use useTheme(), useStyles(), semantic tokens, StyledText, and package controls.
|
|
32
|
-
Do not add app-local Appearance or matchMedia listeners for
|
|
33
|
-
|
|
33
|
+
Do not add app-local Appearance or matchMedia listeners for theme sync. On web
|
|
34
|
+
theme colors are CSS variables: use withAlpha(color, alpha), never hex-suffix
|
|
35
|
+
concatenation.
|
|
34
36
|
|
|
35
37
|
i18n is optional. Plain children and text props work without app i18n. Use
|
|
36
|
-
configureExpoUiI18n() only
|
|
38
|
+
configureExpoUiI18n() only to translate package tx props.
|