@mrmeg/expo-ui 0.24.0 → 0.25.1
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 +26 -1
- package/README.md +46 -2
- package/dist/components/Avatar.js +6 -1
- package/dist/components/Button.d.ts +1 -1
- package/dist/components/Carousel.js +2 -0
- package/dist/components/Dialog.d.ts +15 -93
- package/dist/components/Dialog.js +5 -0
- package/dist/components/DismissKeyboard.d.ts +8 -6
- package/dist/components/DismissKeyboard.js +9 -7
- package/dist/components/DropdownMenu.d.ts +5 -34
- package/dist/components/DropdownMenu.js +2 -1
- package/dist/components/InputOTP.js +2 -0
- package/dist/components/Popover.d.ts +11 -11
- package/dist/components/Popover.js +3 -2
- package/dist/components/Select.d.ts +12 -20
- package/dist/components/Select.js +2 -1
- package/dist/components/StatusBar.js +2 -6
- package/dist/components/StyledText.context.d.ts +1 -1
- package/dist/components/StyledText.d.ts +6 -2
- package/dist/components/TextInput.d.ts +7 -3
- package/dist/components/TextInput.js +35 -28
- package/dist/components/Tooltip.d.ts +10 -24
- package/dist/components/Tooltip.js +5 -2
- package/dist/components/keyboardController.d.ts +4 -2
- package/dist/components/keyboardController.js +4 -0
- package/dist/components/keyboardDismiss.js +126 -55
- package/dist/components/keyboardFocusRegistry.d.ts +6 -4
- package/dist/constants/spacing.d.ts +2 -2
- package/dist/constants/spacing.js +2 -2
- package/dist/design-system.json +2236 -0
- package/dist/hooks/useTheme.d.ts +11 -3
- package/dist/state/themeStore.d.ts +1 -1
- package/package.json +11 -8
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,64 @@
|
|
|
3
3
|
All notable changes to `@mrmeg/expo-ui` are documented here. This project
|
|
4
4
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
5
|
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [0.25.1]
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Android password toggles retain the native field.** The secure/plain Host
|
|
13
|
+
keys previously replaced the Compose field on every eye press, leaving the
|
|
14
|
+
keyboard visible but disconnecting subsequent typing. Android now changes
|
|
15
|
+
password transformation on one mounted Host/input/ref, preserving focus,
|
|
16
|
+
selection, and the input connection. iOS retains its two-field handoff.
|
|
17
|
+
- **Non-scrolling drags no longer trigger tap-away dismissal.** `DismissKeyboard`
|
|
18
|
+
gives its boundary sole tap-dismiss ownership (`keyboardShouldPersistTaps="always"`)
|
|
19
|
+
instead of letting RN ScrollView independently blur a registered hosted field
|
|
20
|
+
on release. Both tap-away dismissal and text-surface focus check final travel
|
|
21
|
+
even when move events are missed, and cancel multitouch. Actual scrolling still
|
|
22
|
+
dismisses through the existing platform-specific scroll behavior.
|
|
23
|
+
|
|
24
|
+
Consumer note: raw `Pressable` labels made from `StyledText` must set
|
|
25
|
+
`selectable={false}`. On Android selectable label text can take native focus from
|
|
26
|
+
an input even when the press handler does not dismiss. The template's auth
|
|
27
|
+
controls and standalone scroll shell are corrected separately in app code;
|
|
28
|
+
updating this package does not update a consuming app's copied forms.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **`spacing.radiusMd` is 10 instead of 12.** Buttons, inputs, selects, toggles,
|
|
33
|
+
tabs, menus, popovers, tooltips, list items and every other component on the
|
|
34
|
+
default radius render slightly squarer. `radiusSm` (8) and `radiusLg` (14) are
|
|
35
|
+
unchanged, so the scale still increases monotonically. Apps that want the old
|
|
36
|
+
look can pass `borderRadius: 12` through `setShape` or a `style` override.
|
|
37
|
+
|
|
38
|
+
## [0.25.0]
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- **The build emits a design-system manifest.** `dist/design-system.json`
|
|
43
|
+
(`schemaVersion: 1`) carries the spacing, radius and icon scales, the palette,
|
|
44
|
+
the theme colors, the font variants, and every component's variant and size
|
|
45
|
+
values, exported as `@mrmeg/expo-ui/design-system.json`.
|
|
46
|
+
`@mrmeg/eslint-plugin-expo-ui` loads it when the design-system sources are not
|
|
47
|
+
on disk, so the design-system rules work against an installed release instead
|
|
48
|
+
of only inside the template repo. Adding an export means the next release is a
|
|
49
|
+
minor bump.
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- **Peer ranges include Expo SDK 58.** `expo`, `expo-font`, `expo-haptics` and
|
|
54
|
+
`@expo/ui` now accept `>=56 <59` (58 pre-releases included), `react-native`
|
|
55
|
+
accepts `>=0.85 <0.89` (0.88 release candidates included), and
|
|
56
|
+
`react-native-gesture-handler` accepts `>=2.30 <4`. SDK 56 and 57 apps stay
|
|
57
|
+
in range; nothing else about the install changes for them.
|
|
58
|
+
- **`StatusBar` no longer sets an Android background color or translucency.**
|
|
59
|
+
React Native 0.88 removed `backgroundColor`, `translucent` and their setters.
|
|
60
|
+
Edge-to-edge has been mandatory since SDK 54, under which the bar was already
|
|
61
|
+
transparent and translucent, so this is a no-op on every supported SDK. The
|
|
62
|
+
component now only applies the theme-driven bar style.
|
|
63
|
+
|
|
6
64
|
## [0.24.0]
|
|
7
65
|
|
|
8
66
|
### Fixed
|
package/LLM_USAGE.md
CHANGED
|
@@ -24,7 +24,8 @@ Importable paths: root, `components`, `components/*`, `constants`,
|
|
|
24
24
|
`constants/*`, `hooks`, `hooks/*`, `state`, `state/*`, `lib`. Never import from
|
|
25
25
|
`@mrmeg/expo-ui/dist/*` or a source checkout path.
|
|
26
26
|
|
|
27
|
-
Hosts: Expo 56–
|
|
27
|
+
Hosts: Expo 56–58 (58 in beta), React 19.2, React Native 0.85–0.88, React
|
|
28
|
+
Native Web 0.21.
|
|
28
29
|
Install the peer versions recommended by the consuming app's Expo SDK.
|
|
29
30
|
|
|
30
31
|
## Required App Setup
|
|
@@ -73,6 +74,13 @@ avoidance is `react-native-keyboard-controller`, so mount its `KeyboardProvider`
|
|
|
73
74
|
above `UIProvider`; use `KeyboardAvoidingView` directly only for a subtree with
|
|
74
75
|
custom behavior.
|
|
75
76
|
|
|
77
|
+
`DismissKeyboard` owns tap dismissal; its ScrollView uses
|
|
78
|
+
`keyboardShouldPersistTaps="always"` so RN cannot independently blur a hosted
|
|
79
|
+
field after a non-scrolling drag. Use `always` for your own scroll view inside
|
|
80
|
+
that boundary too. Dead-space taps dismiss on release; movement and multitouch
|
|
81
|
+
cancel. Actual scrolling retains platform-specific dismissal. Submit handlers
|
|
82
|
+
call `dismissKeyboard()` explicitly.
|
|
83
|
+
|
|
76
84
|
`BottomSheet` renders the platform's native sheet through `@expo/ui` (iOS
|
|
77
85
|
SwiftUI `.sheet()`, Android Material3 `ModalBottomSheet`, web `vaul`). The
|
|
78
86
|
platform owns gestures and keyboard avoidance: `swipeEnabled`, `avoidKeyboard`,
|
|
@@ -184,6 +192,18 @@ faces through `expo-font` / `@expo-google-fonts`, `"numeric"` (default) for one
|
|
|
184
192
|
multi-weight CSS family. `setShape({ button: { borderRadius?, withShadow? } })`
|
|
185
193
|
re-shapes Buttons globally. Per-instance props and caller `style` always win.
|
|
186
194
|
|
|
195
|
+
Enforce these rules mechanically with `@mrmeg/eslint-plugin-expo-ui`
|
|
196
|
+
(`bun add -d @mrmeg/eslint-plugin-expo-ui`), which reports raw colors, off-scale
|
|
197
|
+
spacing and radius, appearance overrides on package components, and raw
|
|
198
|
+
primitives as ESLint diagnostics. It reads this package's
|
|
199
|
+
`design-system.json` manifest out of `node_modules`, so the messages quote the
|
|
200
|
+
tokens, presets and sizes of the installed release. The plugin's first npm release is
|
|
201
|
+
still pending, and only `@mrmeg/expo-ui` releases built after the manifest was
|
|
202
|
+
added ship one: the installed release has it when
|
|
203
|
+
`node_modules/@mrmeg/expo-ui/dist/design-system.json` exists. Config block and
|
|
204
|
+
settings:
|
|
205
|
+
https://raw.githubusercontent.com/mrmeg/expo-template/main/packages/lint/README.md
|
|
206
|
+
|
|
187
207
|
## Component Use-Case Index
|
|
188
208
|
|
|
189
209
|
Check this before creating a new app-local primitive. All components come from
|
|
@@ -242,6 +262,11 @@ Check this before creating a new app-local primitive. All components come from
|
|
|
242
262
|
`BodyText`, `CaptionText`, `LabelText`, `EyebrowText`, `MonoText`, `SerifText`,
|
|
243
263
|
`SansSerifText`, `SerifBoldText`, `SansSerifBoldText`.
|
|
244
264
|
|
|
265
|
+
Set `selectable={false}` on text labels inside app-owned `Pressable`s. Selectable
|
|
266
|
+
Android text can take input focus even if the press handler never dismisses the
|
|
267
|
+
keyboard. Package controls already scope label selectability; ordinary text
|
|
268
|
+
should remain selectable.
|
|
269
|
+
|
|
245
270
|
## Component Selection Rules
|
|
246
271
|
|
|
247
272
|
- `Button` commands · `Toggle` one pressed state · `ToggleGroup` a related set · `Switch` binary settings · `RadioGroup` few exclusive choices · `Select` longer option sets.
|
package/README.md
CHANGED
|
@@ -9,13 +9,24 @@ Read `node_modules/@mrmeg/expo-ui/llms.txt` first, then `LLM_USAGE.md` (concise
|
|
|
9
9
|
rules) or `llms-full.md` (expanded contract), before creating app-local UI
|
|
10
10
|
primitives. All three ship in the npm tarball.
|
|
11
11
|
|
|
12
|
+
The same rules are available as an ESLint plugin: `@mrmeg/eslint-plugin-expo-ui`
|
|
13
|
+
turns the theme and text rules into diagnostics at the call site — raw colors,
|
|
14
|
+
off-scale spacing and radius, appearance overrides on package components, and raw
|
|
15
|
+
primitives that have a wrapper here. See [`../lint/README.md`](../lint/README.md)
|
|
16
|
+
for the config block. It reads the design-system facts from
|
|
17
|
+
`@mrmeg/expo-ui/design-system.json`, the manifest this package's build ships, so
|
|
18
|
+
the diagnostics quote the tokens, presets and sizes of the installed release. The
|
|
19
|
+
plugin's first npm release is still pending, and only `@mrmeg/expo-ui` releases
|
|
20
|
+
built after the manifest was added ship it: the installed release has one when
|
|
21
|
+
`node_modules/@mrmeg/expo-ui/dist/design-system.json` exists.
|
|
22
|
+
|
|
12
23
|
## Install
|
|
13
24
|
|
|
14
25
|
```sh
|
|
15
26
|
bun add @mrmeg/expo-ui
|
|
16
27
|
```
|
|
17
28
|
|
|
18
|
-
Tested hosts: Expo SDK 56–
|
|
29
|
+
Tested hosts: Expo SDK 56–58 (58 in beta), React 19.2, React Native 0.85–0.88, React Native
|
|
19
30
|
Web 0.21. Install these peers at the versions your Expo SDK recommends: `expo`,
|
|
20
31
|
`@expo/ui`, `expo-font`, `expo-haptics`, `react`, `react-native`,
|
|
21
32
|
`react-native-web`, `react-native-gesture-handler`,
|
|
@@ -99,6 +110,20 @@ For a subtree with custom keyboard behavior, use `KeyboardAvoidingView`
|
|
|
99
110
|
directly (`behavior`, `automaticOffset`, `contentContainerStyle`,
|
|
100
111
|
`keyboardVerticalOffset`).
|
|
101
112
|
|
|
113
|
+
### Keyboard dismissal
|
|
114
|
+
|
|
115
|
+
`DismissKeyboard` owns tap-away dismissal without claiming touches. Its scroll
|
|
116
|
+
view uses `keyboardShouldPersistTaps="always"`: RN's `handled` policy can blur
|
|
117
|
+
Expo-hosted fields independently when a finger travels without scrolling.
|
|
118
|
+
Single-finger dead-space taps dismiss on release; movement past 10 logical units
|
|
119
|
+
and multitouch cancel the tap. Real scrolling still dismisses (interactive on
|
|
120
|
+
iOS, an explicit scroll-begin handler on Android).
|
|
121
|
+
|
|
122
|
+
If a form supplies its own ScrollView inside `DismissKeyboard scrollable={false}`
|
|
123
|
+
or an app-level `useKeyboardDismissResponder` boundary, use `always` there too.
|
|
124
|
+
Avoid nested keyboard-avoiding wrappers; the package reuses `UIProvider`'s owner.
|
|
125
|
+
Call `dismissKeyboard()` explicitly from submit handlers.
|
|
126
|
+
|
|
102
127
|
## Theme System
|
|
103
128
|
|
|
104
129
|
```tsx
|
|
@@ -199,7 +224,7 @@ too, because @react-navigation parses them with a color library.
|
|
|
199
224
|
|
|
200
225
|
`spacing` exposes an 8px base scale (`xxs` 2, `xs` 4, `sm` 8, `smd` 12, `md`
|
|
201
226
|
16, `mdl` 20, `lg` 24, `xl` 32, `xxl` 48, `xxxl` 64), radii (`radiusNone` 0,
|
|
202
|
-
`radiusXs` 4, `radiusSm` 8, `radiusMd`
|
|
227
|
+
`radiusXs` 4, `radiusSm` 8, `radiusMd` 10, `radiusLg` 14, `radiusXl` 18,
|
|
203
228
|
`radius2xl` 24, `radiusFull` 9999), icon sizes (`iconXs` 12, `iconSm` 16,
|
|
204
229
|
`iconMd` 24, `iconLg` 32, `iconXl` 48), and the semantic density tokens package
|
|
205
230
|
components read from. Prefer the semantic names so a density tune lands
|
|
@@ -289,6 +314,10 @@ import { BodyText, CaptionText, HeadingText, StyledText } from "@mrmeg/expo-ui/c
|
|
|
289
314
|
- `selectable`: defaults to `true`; package controls disable it for labels and
|
|
290
315
|
interactive chrome where accidental drag selection would feel broken.
|
|
291
316
|
|
|
317
|
+
For app-owned `Pressable` labels, explicitly set `selectable={false}` on the
|
|
318
|
+
nested `StyledText`. On Android selectable label text can take focus and the IME
|
|
319
|
+
connection from an active input. Preserve selection for ordinary readable text.
|
|
320
|
+
|
|
292
321
|
Aliases: `DisplayText`, `TitleText`, `HeadingText`, `SubheadingText`,
|
|
293
322
|
`BodyText`, `CaptionText`, `LabelText`, `EyebrowText`, `MonoText` (code, IDs,
|
|
294
323
|
tabular figures), `SerifText`, `SansSerifText`, `SerifBoldText`,
|
|
@@ -656,3 +685,18 @@ inspected before release. `ui:consumer-smoke` installs the packed tarball into
|
|
|
656
685
|
a clean fixture, checks every documented export-map target resolves,
|
|
657
686
|
type-checks all public entrypoints, and runs an iOS `expo export` against the
|
|
658
687
|
packed package at the workspace's Expo version, without a custom Metro config.
|
|
688
|
+
|
|
689
|
+
### Design-system manifest
|
|
690
|
+
|
|
691
|
+
`ui:build` also writes `dist/design-system.json`
|
|
692
|
+
([`scripts/build-design-system-manifest.mjs`](../../scripts/build-design-system-manifest.mjs)):
|
|
693
|
+
the spacing, radius and icon scales, the palette, the light and dark themes, the
|
|
694
|
+
font variants, and every component's variant and size values, serialized out of
|
|
695
|
+
`src` by the same extractor the lint rules use. It is exported as
|
|
696
|
+
`@mrmeg/expo-ui/design-system.json`, and `@mrmeg/eslint-plugin-expo-ui` reads it
|
|
697
|
+
in projects that install this package instead of checking out its sources — which
|
|
698
|
+
is what lets the design-system rules quote this release's presets and tokens.
|
|
699
|
+
`ui:consumer-smoke` asserts the packed tarball carries it and that it parses at
|
|
700
|
+
`schemaVersion: 1` with a non-empty component list. The build fails rather than
|
|
701
|
+
writing an empty manifest, so a broken extractor cannot ship as "no rules to
|
|
702
|
+
enforce".
|
|
@@ -81,12 +81,17 @@ export function getAvatarInitials(name) {
|
|
|
81
81
|
* `{ bundle, name }`, a resolved asset object). Keying those as "no image"
|
|
82
82
|
* silently degraded a perfectly good source to initials.
|
|
83
83
|
*/
|
|
84
|
+
// `Array.isArray` narrows to `any[]`, which cannot subtract the readonly array
|
|
85
|
+
// arm of the source union, so the predicate spells the arm out.
|
|
86
|
+
function isSourceList(source) {
|
|
87
|
+
return Array.isArray(source);
|
|
88
|
+
}
|
|
84
89
|
function getSourceKey(source) {
|
|
85
90
|
if (source == null)
|
|
86
91
|
return null;
|
|
87
92
|
if (typeof source === "number")
|
|
88
93
|
return `asset:${source}`;
|
|
89
|
-
if (
|
|
94
|
+
if (isSourceList(source)) {
|
|
90
95
|
// An empty array carries no image; anything else does.
|
|
91
96
|
const keys = source.map((entry) => getSourceKey(entry) ?? "").filter(Boolean);
|
|
92
97
|
return keys.length > 0 ? keys.join("|") : null;
|
|
@@ -40,7 +40,7 @@ export interface ButtonProps extends PressableProps {
|
|
|
40
40
|
*/
|
|
41
41
|
txOptions?: TextProps["txOptions"];
|
|
42
42
|
/**
|
|
43
|
-
* An optional style override useful for
|
|
43
|
+
* An optional style override useful for margin and layout.
|
|
44
44
|
*/
|
|
45
45
|
style?: StyleProp<ViewStyle>;
|
|
46
46
|
/**
|
|
@@ -94,6 +94,8 @@ export function Carousel({ children, itemWidth = 0.85, gap = spacing.md, content
|
|
|
94
94
|
// state: web's throttled ticks report the same page many times over, and
|
|
95
95
|
// onIndexChange must fire once per page.
|
|
96
96
|
const activeIndexRef = useRef(activeIndex);
|
|
97
|
+
// `ComponentRef<typeof ScrollView>` rather than RN 0.88's `ScrollViewInstance`,
|
|
98
|
+
// which RN 0.85/0.86 typings do not export.
|
|
97
99
|
const scrollRef = useRef(null);
|
|
98
100
|
const hasScrolledToInitial = useRef(false);
|
|
99
101
|
const commitIndex = useCallback((next) => {
|
|
@@ -1,25 +1,9 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { ViewProps } from "react-native";
|
|
3
3
|
import * as DialogPrimitive from "@rn-primitives/dialog";
|
|
4
4
|
import * as AlertDialogPrimitive from "@rn-primitives/alert-dialog";
|
|
5
|
-
declare const DialogTrigger:
|
|
6
|
-
|
|
7
|
-
asChild?: boolean;
|
|
8
|
-
} & {
|
|
9
|
-
onKeyDown?: (ev: React.KeyboardEvent) => void;
|
|
10
|
-
onKeyUp?: (ev: React.KeyboardEvent) => void;
|
|
11
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
12
|
-
displayName: string;
|
|
13
|
-
};
|
|
14
|
-
declare const DialogClose: {
|
|
15
|
-
({ asChild, onPress: onPressProp, disabled, ref, ...props }: Omit<import("react-native").PressableProps & React.RefAttributes<View>, "ref"> & {
|
|
16
|
-
asChild?: boolean;
|
|
17
|
-
} & {
|
|
18
|
-
onKeyDown?: (ev: React.KeyboardEvent) => void;
|
|
19
|
-
onKeyUp?: (ev: React.KeyboardEvent) => void;
|
|
20
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
21
|
-
displayName: string;
|
|
22
|
-
};
|
|
5
|
+
declare const DialogTrigger: typeof DialogPrimitive.Trigger;
|
|
6
|
+
declare const DialogClose: typeof DialogPrimitive.Close;
|
|
23
7
|
interface DialogProps extends DialogPrimitive.RootProps {
|
|
24
8
|
children: React.ReactNode;
|
|
25
9
|
}
|
|
@@ -36,58 +20,19 @@ declare function DialogTitle({ children, style, ...props }: DialogPrimitive.Titl
|
|
|
36
20
|
declare function DialogDescription({ children, style, ...props }: DialogPrimitive.DescriptionProps & {
|
|
37
21
|
children: React.ReactNode;
|
|
38
22
|
}): React.JSX.Element;
|
|
39
|
-
|
|
40
|
-
Trigger:
|
|
41
|
-
({ asChild, onPress: onPressProp, disabled, ref, ...props }: Omit<import("react-native").PressableProps & React.RefAttributes<View>, "ref"> & {
|
|
42
|
-
asChild?: boolean;
|
|
43
|
-
} & {
|
|
44
|
-
onKeyDown?: (ev: React.KeyboardEvent) => void;
|
|
45
|
-
onKeyUp?: (ev: React.KeyboardEvent) => void;
|
|
46
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
47
|
-
displayName: string;
|
|
48
|
-
};
|
|
23
|
+
type DialogComponent = typeof DialogRoot & {
|
|
24
|
+
Trigger: typeof DialogTrigger;
|
|
49
25
|
Content: typeof DialogContent;
|
|
50
26
|
Header: typeof DialogHeader;
|
|
51
27
|
Footer: typeof DialogFooter;
|
|
52
28
|
Title: typeof DialogTitle;
|
|
53
29
|
Description: typeof DialogDescription;
|
|
54
|
-
Close:
|
|
55
|
-
({ asChild, onPress: onPressProp, disabled, ref, ...props }: Omit<import("react-native").PressableProps & React.RefAttributes<View>, "ref"> & {
|
|
56
|
-
asChild?: boolean;
|
|
57
|
-
} & {
|
|
58
|
-
onKeyDown?: (ev: React.KeyboardEvent) => void;
|
|
59
|
-
onKeyUp?: (ev: React.KeyboardEvent) => void;
|
|
60
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
61
|
-
displayName: string;
|
|
62
|
-
};
|
|
63
|
-
};
|
|
64
|
-
declare const AlertDialogTrigger: {
|
|
65
|
-
({ asChild, onPress: onPressProp, disabled, ref, ...props }: Omit<import("react-native").PressableProps & React.RefAttributes<View>, "ref"> & {
|
|
66
|
-
asChild?: boolean;
|
|
67
|
-
} & {
|
|
68
|
-
onKeyDown?: (ev: React.KeyboardEvent) => void;
|
|
69
|
-
onKeyUp?: (ev: React.KeyboardEvent) => void;
|
|
70
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
71
|
-
displayName: string;
|
|
72
|
-
};
|
|
73
|
-
declare const AlertDialogAction: {
|
|
74
|
-
({ asChild, onPress: onPressProp, disabled, ref, ...props }: Omit<import("react-native").PressableProps & React.RefAttributes<View>, "ref"> & {
|
|
75
|
-
asChild?: boolean;
|
|
76
|
-
} & {
|
|
77
|
-
onKeyDown?: (ev: React.KeyboardEvent) => void;
|
|
78
|
-
onKeyUp?: (ev: React.KeyboardEvent) => void;
|
|
79
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
80
|
-
displayName: string;
|
|
81
|
-
};
|
|
82
|
-
declare const AlertDialogCancel: {
|
|
83
|
-
({ asChild, onPress: onPressProp, disabled, ref, ...props }: Omit<import("react-native").PressableProps & React.RefAttributes<View>, "ref"> & {
|
|
84
|
-
asChild?: boolean;
|
|
85
|
-
} & {
|
|
86
|
-
onKeyDown?: (ev: React.KeyboardEvent) => void;
|
|
87
|
-
onKeyUp?: (ev: React.KeyboardEvent) => void;
|
|
88
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
89
|
-
displayName: string;
|
|
30
|
+
Close: typeof DialogClose;
|
|
90
31
|
};
|
|
32
|
+
declare const Dialog: DialogComponent;
|
|
33
|
+
declare const AlertDialogTrigger: typeof AlertDialogPrimitive.Trigger;
|
|
34
|
+
declare const AlertDialogAction: typeof AlertDialogPrimitive.Action;
|
|
35
|
+
declare const AlertDialogCancel: typeof AlertDialogPrimitive.Cancel;
|
|
91
36
|
interface AlertDialogProps extends AlertDialogPrimitive.RootProps {
|
|
92
37
|
children: React.ReactNode;
|
|
93
38
|
}
|
|
@@ -102,39 +47,16 @@ declare function AlertDialogTitle({ children, style, ...props }: AlertDialogPrim
|
|
|
102
47
|
declare function AlertDialogDescription({ children, style, ...props }: AlertDialogPrimitive.DescriptionProps & {
|
|
103
48
|
children: React.ReactNode;
|
|
104
49
|
}): React.JSX.Element;
|
|
105
|
-
|
|
106
|
-
Trigger:
|
|
107
|
-
({ asChild, onPress: onPressProp, disabled, ref, ...props }: Omit<import("react-native").PressableProps & React.RefAttributes<View>, "ref"> & {
|
|
108
|
-
asChild?: boolean;
|
|
109
|
-
} & {
|
|
110
|
-
onKeyDown?: (ev: React.KeyboardEvent) => void;
|
|
111
|
-
onKeyUp?: (ev: React.KeyboardEvent) => void;
|
|
112
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
113
|
-
displayName: string;
|
|
114
|
-
};
|
|
50
|
+
type AlertDialogComponent = typeof AlertDialogRoot & {
|
|
51
|
+
Trigger: typeof AlertDialogTrigger;
|
|
115
52
|
Content: typeof AlertDialogContent;
|
|
116
53
|
Header: typeof DialogHeader;
|
|
117
54
|
Footer: typeof DialogFooter;
|
|
118
55
|
Title: typeof AlertDialogTitle;
|
|
119
56
|
Description: typeof AlertDialogDescription;
|
|
120
|
-
Action:
|
|
121
|
-
|
|
122
|
-
asChild?: boolean;
|
|
123
|
-
} & {
|
|
124
|
-
onKeyDown?: (ev: React.KeyboardEvent) => void;
|
|
125
|
-
onKeyUp?: (ev: React.KeyboardEvent) => void;
|
|
126
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
127
|
-
displayName: string;
|
|
128
|
-
};
|
|
129
|
-
Cancel: {
|
|
130
|
-
({ asChild, onPress: onPressProp, disabled, ref, ...props }: Omit<import("react-native").PressableProps & React.RefAttributes<View>, "ref"> & {
|
|
131
|
-
asChild?: boolean;
|
|
132
|
-
} & {
|
|
133
|
-
onKeyDown?: (ev: React.KeyboardEvent) => void;
|
|
134
|
-
onKeyUp?: (ev: React.KeyboardEvent) => void;
|
|
135
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
136
|
-
displayName: string;
|
|
137
|
-
};
|
|
57
|
+
Action: typeof AlertDialogAction;
|
|
58
|
+
Cancel: typeof AlertDialogCancel;
|
|
138
59
|
};
|
|
60
|
+
declare const AlertDialog: AlertDialogComponent;
|
|
139
61
|
export { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, DialogClose, AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel, };
|
|
140
62
|
export type { DialogProps, DialogContentProps, AlertDialogProps, AlertDialogContentProps, };
|
|
@@ -14,6 +14,11 @@ const FullWindowOverlay = Platform.OS === "ios" ? RNFullWindowOverlay : React.Fr
|
|
|
14
14
|
// ============================================================================
|
|
15
15
|
// Dialog
|
|
16
16
|
// ============================================================================
|
|
17
|
+
// These re-exports and the compound components below carry explicit type
|
|
18
|
+
// annotations because React Native 0.88 resolves primitive ref types through
|
|
19
|
+
// its private `ReactNativeElement`, which declaration emit cannot name
|
|
20
|
+
// portably (TS2883). Annotating with `typeof <primitive>` keeps the emitted
|
|
21
|
+
// .d.ts pointing at the primitive's own public types instead.
|
|
17
22
|
const DialogTrigger = DialogPrimitive.Trigger;
|
|
18
23
|
const DialogClose = DialogPrimitive.Close;
|
|
19
24
|
function DialogRoot({ children, ...props }) {
|
|
@@ -12,15 +12,17 @@ type Props = {
|
|
|
12
12
|
* Wrapper for a screen (or form) that dismisses the keyboard when the user taps
|
|
13
13
|
* outside of a text input.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
* field
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
15
|
+
* The wrapper never claims the touch: buttons, other fields and the focused
|
|
16
|
+
* field's own gestures win. It dismisses only an unclaimed single-finger tap
|
|
17
|
+
* within the travel slop, even if no scroll/move event was delivered. The inner
|
|
18
|
+
* ScrollView uses `always` to leave tap dismissal to this boundary; RN's
|
|
19
|
+
* `handled` policy can independently blur registered hosted inputs on release
|
|
20
|
+
* after a drag that produced no scroll. See `useKeyboardDismissResponder`.
|
|
20
21
|
*
|
|
21
22
|
* A drag on the inner ScrollView also hides the keyboard: `interactive` on iOS
|
|
22
23
|
* (UIKit resigns any first responder, SwiftUI fields included) and an explicit
|
|
23
|
-
* dismiss on drag start on Android,
|
|
24
|
+
* dismiss on drag start on Android, using the package's window-independent
|
|
25
|
+
* native blur handle with a keyboard-controller fallback.
|
|
24
26
|
*/
|
|
25
27
|
export declare function DismissKeyboard({ children, style, avoidKeyboard, scrollable, }: Props): React.JSX.Element;
|
|
26
28
|
export {};
|
|
@@ -6,20 +6,22 @@ import { dismissKeyboard, useKeyboardDismissResponder } from "./keyboardDismiss.
|
|
|
6
6
|
* Wrapper for a screen (or form) that dismisses the keyboard when the user taps
|
|
7
7
|
* outside of a text input.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
* field
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* The wrapper never claims the touch: buttons, other fields and the focused
|
|
10
|
+
* field's own gestures win. It dismisses only an unclaimed single-finger tap
|
|
11
|
+
* within the travel slop, even if no scroll/move event was delivered. The inner
|
|
12
|
+
* ScrollView uses `always` to leave tap dismissal to this boundary; RN's
|
|
13
|
+
* `handled` policy can independently blur registered hosted inputs on release
|
|
14
|
+
* after a drag that produced no scroll. See `useKeyboardDismissResponder`.
|
|
14
15
|
*
|
|
15
16
|
* A drag on the inner ScrollView also hides the keyboard: `interactive` on iOS
|
|
16
17
|
* (UIKit resigns any first responder, SwiftUI fields included) and an explicit
|
|
17
|
-
* dismiss on drag start on Android,
|
|
18
|
+
* dismiss on drag start on Android, using the package's window-independent
|
|
19
|
+
* native blur handle with a keyboard-controller fallback.
|
|
18
20
|
*/
|
|
19
21
|
export function DismissKeyboard({ children, style, avoidKeyboard = true, scrollable = true, }) {
|
|
20
22
|
const responderProps = useKeyboardDismissResponder();
|
|
21
23
|
const hasKeyboardAvoidance = useKeyboardAvoidance();
|
|
22
|
-
const content = scrollable ? (_jsx(ScrollView, { style: { flex: 1 }, contentContainerStyle: { flexGrow: 1, justifyContent: "center" }, keyboardShouldPersistTaps: "
|
|
24
|
+
const content = scrollable ? (_jsx(ScrollView, { style: { flex: 1 }, contentContainerStyle: { flexGrow: 1, justifyContent: "center" }, keyboardShouldPersistTaps: "always", keyboardDismissMode: Platform.OS === "ios" ? "interactive" : "none", onScrollBeginDrag: Platform.OS === "android" ? dismissKeyboard : undefined, showsVerticalScrollIndicator: false, children: children })) : (children);
|
|
23
25
|
if (!avoidKeyboard || hasKeyboardAvoidance) {
|
|
24
26
|
return (_jsx(View, { style: { flex: 1 }, ...responderProps, children: content }));
|
|
25
27
|
}
|
|
@@ -1,40 +1,11 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { type TextStyle
|
|
2
|
+
import { type TextStyle } from "react-native";
|
|
3
3
|
import * as DropdownMenuPrimitive from "@rn-primitives/dropdown-menu";
|
|
4
|
-
declare const DropdownMenu:
|
|
5
|
-
|
|
6
|
-
asChild?: boolean;
|
|
7
|
-
} & {
|
|
8
|
-
onOpenChange?: (open: boolean) => void;
|
|
9
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
10
|
-
displayName: string;
|
|
11
|
-
};
|
|
12
|
-
declare const DropdownMenuGroup: {
|
|
13
|
-
({ asChild, ref, ...props }: import("react-native").ViewProps & {
|
|
14
|
-
asChild?: boolean;
|
|
15
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
16
|
-
displayName: string;
|
|
17
|
-
};
|
|
4
|
+
declare const DropdownMenu: typeof DropdownMenuPrimitive.Root;
|
|
5
|
+
declare const DropdownMenuGroup: typeof DropdownMenuPrimitive.Group;
|
|
18
6
|
declare const DropdownMenuPortal: typeof DropdownMenuPrimitive.Portal;
|
|
19
|
-
declare const DropdownMenuSub:
|
|
20
|
-
|
|
21
|
-
asChild?: boolean;
|
|
22
|
-
} & {
|
|
23
|
-
defaultOpen?: boolean;
|
|
24
|
-
open?: boolean;
|
|
25
|
-
onOpenChange?: (value: boolean) => void;
|
|
26
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
27
|
-
displayName: string;
|
|
28
|
-
};
|
|
29
|
-
declare const DropdownMenuRadioGroup: {
|
|
30
|
-
({ asChild, value, onValueChange, ref, ...props }: import("react-native").ViewProps & {
|
|
31
|
-
asChild?: boolean;
|
|
32
|
-
} & {
|
|
33
|
-
value: string | undefined;
|
|
34
|
-
onValueChange: (value: string) => void;
|
|
35
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
36
|
-
displayName: string;
|
|
37
|
-
};
|
|
7
|
+
declare const DropdownMenuSub: typeof DropdownMenuPrimitive.Sub;
|
|
8
|
+
declare const DropdownMenuRadioGroup: typeof DropdownMenuPrimitive.RadioGroup;
|
|
38
9
|
/**
|
|
39
10
|
* DropdownMenuTrigger Component
|
|
40
11
|
* Wraps the primitive Trigger to default `accessibilityRole="button"`.
|
|
@@ -10,7 +10,8 @@ import { useScalePress } from "../hooks/useScalePress.js";
|
|
|
10
10
|
import * as DropdownMenuPrimitive from "@rn-primitives/dropdown-menu";
|
|
11
11
|
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens";
|
|
12
12
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
13
|
-
// Re-export primitives that don't need styling
|
|
13
|
+
// Re-export primitives that don't need styling. Annotated with `typeof` for
|
|
14
|
+
// portable declaration emit — see the note in Dialog.tsx (TS2883).
|
|
14
15
|
const DropdownMenu = DropdownMenuPrimitive.Root;
|
|
15
16
|
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
16
17
|
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
@@ -27,6 +27,8 @@ const BULLET = "\u2022";
|
|
|
27
27
|
*/
|
|
28
28
|
function InputOTP({ length = 6, value = "", onChangeText, onComplete, error = false, errorText, disabled = false, autoFocus = false, secureTextEntry = false, inputMode = "numeric", style: styleOverride, }) {
|
|
29
29
|
const { theme } = useTheme();
|
|
30
|
+
// `ComponentRef<typeof RNTextInput>` rather than RN 0.88's `TextInputInstance`,
|
|
31
|
+
// which RN 0.85/0.86 typings do not export.
|
|
30
32
|
const inputRef = useRef(null);
|
|
31
33
|
const [focused, setFocused] = useState(false);
|
|
32
34
|
const styles = themedStyles(theme);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as PopoverPrimitive from "@rn-primitives/popover";
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { ViewProps } from "react-native";
|
|
4
4
|
/**
|
|
5
5
|
* Popover Trigger Component
|
|
6
6
|
* The element that triggers the popover to open/close
|
|
@@ -44,21 +44,21 @@ interface PopoverFooterProps extends ViewProps {
|
|
|
44
44
|
}
|
|
45
45
|
declare function PopoverFooter({ children, style, ...props }: PopoverFooterProps): React.JSX.Element;
|
|
46
46
|
/**
|
|
47
|
-
* Popover Component
|
|
48
|
-
*
|
|
47
|
+
* Popover Root Component
|
|
48
|
+
* Manages popover state and provides context for trigger and content
|
|
49
49
|
*/
|
|
50
|
-
declare const
|
|
51
|
-
|
|
52
|
-
asChild?: boolean;
|
|
53
|
-
} & {
|
|
54
|
-
onOpenChange?: (open: boolean) => void;
|
|
55
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
56
|
-
displayName: string;
|
|
57
|
-
} & {
|
|
50
|
+
declare const PopoverRoot: typeof PopoverPrimitive.Root;
|
|
51
|
+
type PopoverComponent = typeof PopoverRoot & {
|
|
58
52
|
Trigger: typeof PopoverTrigger;
|
|
59
53
|
Content: typeof PopoverContent;
|
|
60
54
|
Header: typeof PopoverHeader;
|
|
61
55
|
Body: typeof PopoverBody;
|
|
62
56
|
Footer: typeof PopoverFooter;
|
|
63
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* Popover Component with Sub-components
|
|
60
|
+
* Properly typed interface for dot notation access (e.g., Popover.Trigger).
|
|
61
|
+
* The annotation is what keeps declaration emit portable — see Dialog.tsx (TS2883).
|
|
62
|
+
*/
|
|
63
|
+
declare const Popover: PopoverComponent;
|
|
64
64
|
export { Popover, PopoverTrigger, PopoverContent, PopoverHeader, PopoverBody, PopoverFooter, };
|
|
@@ -40,7 +40,7 @@ function PopoverContent({ side, align = "center", sideOffset = 4, portalHost, ..
|
|
|
40
40
|
padding: spacing.xs,
|
|
41
41
|
...getShadowStyle("soft"),
|
|
42
42
|
},
|
|
43
|
-
]);
|
|
43
|
+
]) ?? undefined;
|
|
44
44
|
return (_jsx(PopoverPrimitive.Portal, { hostName: portalHost, children: _jsx(FullWindowOverlay, { children: _jsx(PopoverPrimitive.Overlay, { style: Platform.select({ native: StyleSheet.absoluteFill }), children: _jsx(AnimatedView, { type: "fade", enterDuration: 200, children: _jsx(TextColorContext.Provider, { value: textColor, children: _jsx(TextClassContext.Provider, { value: "", children: _jsx(PopoverPrimitive.Content, { side: side, align: align, sideOffset: sideOffset, style: contentStyle, ...props }) }) }) }) }) }) }));
|
|
45
45
|
}
|
|
46
46
|
function PopoverHeader({ children, style, ...props }) {
|
|
@@ -83,7 +83,8 @@ function PopoverFooter({ children, style, ...props }) {
|
|
|
83
83
|
const PopoverRoot = PopoverPrimitive.Root;
|
|
84
84
|
/**
|
|
85
85
|
* Popover Component with Sub-components
|
|
86
|
-
* Properly typed interface for dot notation access (e.g., Popover.Trigger)
|
|
86
|
+
* Properly typed interface for dot notation access (e.g., Popover.Trigger).
|
|
87
|
+
* The annotation is what keeps declaration emit portable — see Dialog.tsx (TS2883).
|
|
87
88
|
*/
|
|
88
89
|
const Popover = Object.assign(PopoverRoot, {
|
|
89
90
|
Trigger: PopoverTrigger,
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { View } from "react-native";
|
|
3
2
|
import * as SelectPrimitive from "@rn-primitives/select";
|
|
3
|
+
/**
|
|
4
|
+
* SelectRoot Component
|
|
5
|
+
* Manages select state and provides context for trigger and content
|
|
6
|
+
*/
|
|
7
|
+
declare const SelectRoot: typeof SelectPrimitive.Root;
|
|
4
8
|
/**
|
|
5
9
|
* Size variants for SelectTrigger
|
|
6
10
|
*/
|
|
@@ -57,25 +61,7 @@ declare function SelectLabel({ style: styleOverride, ...props }: SelectLabelProp
|
|
|
57
61
|
*/
|
|
58
62
|
type SelectSeparatorProps = SelectPrimitive.SeparatorProps;
|
|
59
63
|
declare function SelectSeparator({ style: styleOverride, ...props }: SelectSeparatorProps): React.JSX.Element;
|
|
60
|
-
|
|
61
|
-
* Select Component with Sub-components
|
|
62
|
-
* Properly typed interface for dot notation access (e.g., Select.Trigger)
|
|
63
|
-
*/
|
|
64
|
-
declare const Select: {
|
|
65
|
-
({ asChild, value: valueProp, defaultValue, onValueChange: onValueChangeProp, onOpenChange: onOpenChangeProp, disabled, ref, ...viewProps }: import("react-native").ViewProps & {
|
|
66
|
-
asChild?: boolean;
|
|
67
|
-
} & {
|
|
68
|
-
value?: import("@rn-primitives/select").Option;
|
|
69
|
-
defaultValue?: import("@rn-primitives/select").Option;
|
|
70
|
-
onValueChange?: (option: import("@rn-primitives/select").Option) => void;
|
|
71
|
-
onOpenChange?: (open: boolean) => void;
|
|
72
|
-
disabled?: boolean;
|
|
73
|
-
dir?: "ltr" | "rtl";
|
|
74
|
-
name?: string;
|
|
75
|
-
required?: boolean;
|
|
76
|
-
} & React.RefAttributes<View>): React.JSX.Element;
|
|
77
|
-
displayName: string;
|
|
78
|
-
} & {
|
|
64
|
+
type SelectComponent = typeof SelectRoot & {
|
|
79
65
|
Trigger: typeof SelectTrigger;
|
|
80
66
|
Value: typeof SelectValue;
|
|
81
67
|
Content: typeof SelectContent;
|
|
@@ -84,5 +70,11 @@ declare const Select: {
|
|
|
84
70
|
Label: typeof SelectLabel;
|
|
85
71
|
Separator: typeof SelectSeparator;
|
|
86
72
|
};
|
|
73
|
+
/**
|
|
74
|
+
* Select Component with Sub-components
|
|
75
|
+
* Properly typed interface for dot notation access (e.g., Select.Trigger).
|
|
76
|
+
* The annotation is what keeps declaration emit portable — see Dialog.tsx (TS2883).
|
|
77
|
+
*/
|
|
78
|
+
declare const Select: SelectComponent;
|
|
87
79
|
export { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SelectGroup, SelectLabel, SelectSeparator, };
|
|
88
80
|
export type { SelectSize, SelectTriggerProps, SelectValueProps, SelectContentProps, SelectItemProps, SelectGroupProps, SelectLabelProps, SelectSeparatorProps, };
|
|
@@ -210,7 +210,8 @@ const styles = StyleSheet.create({
|
|
|
210
210
|
});
|
|
211
211
|
/**
|
|
212
212
|
* Select Component with Sub-components
|
|
213
|
-
* Properly typed interface for dot notation access (e.g., Select.Trigger)
|
|
213
|
+
* Properly typed interface for dot notation access (e.g., Select.Trigger).
|
|
214
|
+
* The annotation is what keeps declaration emit portable — see Dialog.tsx (TS2883).
|
|
214
215
|
*/
|
|
215
216
|
const Select = Object.assign(SelectRoot, {
|
|
216
217
|
Trigger: SelectTrigger,
|