@expo/ui 56.0.7 → 56.0.9
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 +39 -1
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +44 -3
- package/android/src/main/java/expo/modules/ui/HostView.kt +2 -0
- package/android/src/main/java/expo/modules/ui/LoadingView.kt +80 -0
- package/android/src/main/java/expo/modules/ui/ModifierRegistry.kt +50 -4
- package/android/src/main/java/expo/modules/ui/RNHostView.kt +8 -3
- package/android/src/main/java/expo/modules/ui/ShadowNodeSyncFlush.kt +28 -0
- package/android/src/main/java/expo/modules/ui/SnackbarView.kt +126 -0
- package/android/src/main/java/expo/modules/ui/state/ObservableState.kt +10 -0
- package/assets/keyboard_arrow_down.xml +10 -0
- package/build/State/useNativeState.d.ts +32 -3
- package/build/State/useNativeState.d.ts.map +1 -1
- package/build/community/bottom-sheet/BottomSheet.ios.d.ts.map +1 -1
- package/build/community/menu/MenuView.android.d.ts +16 -0
- package/build/community/menu/MenuView.android.d.ts.map +1 -0
- package/build/community/menu/MenuView.d.ts +19 -0
- package/build/community/menu/MenuView.d.ts.map +1 -0
- package/build/community/menu/MenuView.ios.d.ts +10 -0
- package/build/community/menu/MenuView.ios.d.ts.map +1 -0
- package/build/community/menu/index.d.ts +5 -0
- package/build/community/menu/index.d.ts.map +1 -0
- package/build/community/menu/types.d.ts +166 -0
- package/build/community/menu/types.d.ts.map +1 -0
- package/build/jetpack-compose/LoadingIndicator/index.d.ts +41 -0
- package/build/jetpack-compose/LoadingIndicator/index.d.ts.map +1 -0
- package/build/jetpack-compose/Snackbar/index.d.ts +94 -0
- package/build/jetpack-compose/Snackbar/index.d.ts.map +1 -0
- package/build/jetpack-compose/index.d.ts +2 -0
- package/build/jetpack-compose/index.d.ts.map +1 -1
- package/build/jetpack-compose/modifiers/index.d.ts +21 -2
- package/build/jetpack-compose/modifiers/index.d.ts.map +1 -1
- package/build/swift-ui/Alert/index.d.ts +42 -0
- package/build/swift-ui/Alert/index.d.ts.map +1 -0
- package/build/swift-ui/BottomSheet/index.d.ts +5 -1
- package/build/swift-ui/BottomSheet/index.d.ts.map +1 -1
- package/build/swift-ui/SlotView.d.ts +5 -2
- package/build/swift-ui/SlotView.d.ts.map +1 -1
- package/build/swift-ui/SwipeActions/index.d.ts +38 -0
- package/build/swift-ui/SwipeActions/index.d.ts.map +1 -0
- package/build/swift-ui/index.d.ts +3 -0
- package/build/swift-ui/index.d.ts.map +1 -1
- package/build/swift-ui/modifiers/index.d.ts +3 -1
- package/build/swift-ui/modifiers/index.d.ts.map +1 -1
- package/build/swift-ui/modifiers/symbolEffect.d.ts +103 -0
- package/build/swift-ui/modifiers/symbolEffect.d.ts.map +1 -0
- package/build/swift-ui/withAnimation.d.ts +26 -0
- package/build/swift-ui/withAnimation.d.ts.map +1 -0
- package/build/universal/BottomSheet/index.android.d.ts +1 -1
- package/build/universal/BottomSheet/index.android.d.ts.map +1 -1
- package/build/universal/BottomSheet/index.d.ts +1 -1
- package/build/universal/BottomSheet/index.d.ts.map +1 -1
- package/build/universal/BottomSheet/index.ios.d.ts +1 -1
- package/build/universal/BottomSheet/index.ios.d.ts.map +1 -1
- package/build/universal/BottomSheet/types.d.ts +27 -0
- package/build/universal/BottomSheet/types.d.ts.map +1 -1
- package/build/universal/Collapsible/index.android.d.ts +8 -0
- package/build/universal/Collapsible/index.android.d.ts.map +1 -0
- package/build/universal/Collapsible/index.d.ts +8 -0
- package/build/universal/Collapsible/index.d.ts.map +1 -0
- package/build/universal/Collapsible/index.ios.d.ts +7 -0
- package/build/universal/Collapsible/index.ios.d.ts.map +1 -0
- package/build/universal/Collapsible/types.d.ts +23 -0
- package/build/universal/Collapsible/types.d.ts.map +1 -0
- package/build/universal/Column/index.d.ts.map +1 -1
- package/build/universal/Host/index.d.ts +5 -7
- package/build/universal/Host/index.d.ts.map +1 -1
- package/build/universal/Host/types.d.ts +72 -0
- package/build/universal/Host/types.d.ts.map +1 -0
- package/build/universal/List/index.android.d.ts +9 -0
- package/build/universal/List/index.android.d.ts.map +1 -0
- package/build/universal/List/index.d.ts +8 -0
- package/build/universal/List/index.d.ts.map +1 -0
- package/build/universal/List/index.ios.d.ts +8 -0
- package/build/universal/List/index.ios.d.ts.map +1 -0
- package/build/universal/List/types.d.ts +26 -0
- package/build/universal/List/types.d.ts.map +1 -0
- package/build/universal/ListItem/ListItem.android.d.ts +8 -0
- package/build/universal/ListItem/ListItem.android.d.ts.map +1 -0
- package/build/universal/ListItem/ListItem.d.ts +9 -0
- package/build/universal/ListItem/ListItem.d.ts.map +1 -0
- package/build/universal/ListItem/ListItem.ios.d.ts +8 -0
- package/build/universal/ListItem/ListItem.ios.d.ts.map +1 -0
- package/build/universal/ListItem/ListItemSlots.d.ts +21 -0
- package/build/universal/ListItem/ListItemSlots.d.ts.map +1 -0
- package/build/universal/ListItem/index.d.ts +10 -0
- package/build/universal/ListItem/index.d.ts.map +1 -0
- package/build/universal/ListItem/types.d.ts +59 -0
- package/build/universal/ListItem/types.d.ts.map +1 -0
- package/build/universal/Picker/Picker.android.d.ts +9 -0
- package/build/universal/Picker/Picker.android.d.ts.map +1 -0
- package/build/universal/Picker/Picker.d.ts +8 -0
- package/build/universal/Picker/Picker.d.ts.map +1 -0
- package/build/universal/Picker/Picker.ios.d.ts +9 -0
- package/build/universal/Picker/Picker.ios.d.ts.map +1 -0
- package/build/universal/Picker/PickerItem.d.ts +9 -0
- package/build/universal/Picker/PickerItem.d.ts.map +1 -0
- package/build/universal/Picker/index.d.ts +8 -0
- package/build/universal/Picker/index.d.ts.map +1 -0
- package/build/universal/Picker/types.d.ts +69 -0
- package/build/universal/Picker/types.d.ts.map +1 -0
- package/build/universal/index.d.ts +4 -0
- package/build/universal/index.d.ts.map +1 -1
- package/expo-module.config.json +1 -1
- package/ios/Alert/Alert.swift +56 -0
- package/ios/Alert/AlertProps.swift +8 -0
- package/ios/BottomSheetView.swift +4 -1
- package/ios/ExpoUIModule.swift +43 -1
- package/ios/ExpoUITouchHandlerHelper.h +4 -1
- package/ios/ExpoUITouchHandlerHelper.mm +1 -0
- package/ios/Modifiers/AnimationConfig.swift +109 -0
- package/ios/Modifiers/SwipeActionsModifier.swift +97 -0
- package/ios/Modifiers/SymbolEffectModifier.swift +452 -0
- package/ios/Modifiers/ViewModifierRegistry.swift +5 -112
- package/ios/SlotView.swift +5 -0
- package/ios/State/ObservableState.swift +12 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.7/expo.modules.ui-56.0.7-sources.jar → 56.0.9/expo.modules.ui-56.0.9-sources.jar} +0 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9-sources.jar.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9-sources.jar.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9-sources.jar.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9-sources.jar.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.aar +0 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.aar.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.aar.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.aar.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.aar.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.7/expo.modules.ui-56.0.7.module → 56.0.9/expo.modules.ui-56.0.9.module} +22 -22
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.module.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.module.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.module.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.module.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.7/expo.modules.ui-56.0.7.pom → 56.0.9/expo.modules.ui-56.0.9.pom} +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.pom.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.pom.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.pom.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.pom.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
- package/package.json +7 -3
- package/src/State/useNativeState.ts +70 -10
- package/src/community/bottom-sheet/BottomSheet.ios.tsx +0 -17
- package/src/community/menu/MenuView.android.tsx +224 -0
- package/src/community/menu/MenuView.ios.tsx +149 -0
- package/src/community/menu/MenuView.tsx +36 -0
- package/src/community/menu/index.tsx +14 -0
- package/src/community/menu/types.tsx +171 -0
- package/src/jetpack-compose/LoadingIndicator/index.tsx +92 -0
- package/src/jetpack-compose/Snackbar/index.tsx +135 -0
- package/src/jetpack-compose/index.ts +2 -0
- package/src/jetpack-compose/modifiers/index.ts +30 -2
- package/src/swift-ui/Alert/index.tsx +87 -0
- package/src/swift-ui/BottomSheet/index.tsx +32 -15
- package/src/swift-ui/SlotView.tsx +17 -4
- package/src/swift-ui/SwipeActions/index.tsx +73 -0
- package/src/swift-ui/index.tsx +3 -0
- package/src/swift-ui/modifiers/index.ts +3 -0
- package/src/swift-ui/modifiers/symbolEffect.ts +181 -0
- package/src/swift-ui/withAnimation.ts +71 -0
- package/src/ts-declarations/react-native-web.d.ts +27 -0
- package/src/universal/BottomSheet/index.android.tsx +27 -3
- package/src/universal/BottomSheet/index.ios.tsx +30 -12
- package/src/universal/BottomSheet/index.tsx +46 -4
- package/src/universal/BottomSheet/types.ts +25 -0
- package/src/universal/Collapsible/index.android.tsx +72 -0
- package/src/universal/Collapsible/index.ios.tsx +16 -0
- package/src/universal/Collapsible/index.tsx +58 -0
- package/src/universal/Collapsible/types.ts +25 -0
- package/src/universal/Column/index.tsx +3 -1
- package/src/universal/Host/index.tsx +69 -5
- package/src/universal/Host/types.ts +70 -0
- package/src/universal/List/index.android.tsx +44 -0
- package/src/universal/List/index.ios.tsx +19 -0
- package/src/universal/List/index.tsx +26 -0
- package/src/universal/List/types.ts +28 -0
- package/src/universal/ListItem/ListItem.android.tsx +52 -0
- package/src/universal/ListItem/ListItem.ios.tsx +58 -0
- package/src/universal/ListItem/ListItem.tsx +72 -0
- package/src/universal/ListItem/ListItemSlots.tsx +66 -0
- package/src/universal/ListItem/index.ts +15 -0
- package/src/universal/ListItem/types.ts +67 -0
- package/src/universal/Picker/Picker.android.tsx +69 -0
- package/src/universal/Picker/Picker.ios.tsx +45 -0
- package/src/universal/Picker/Picker.tsx +52 -0
- package/src/universal/Picker/PickerItem.tsx +27 -0
- package/src/universal/Picker/index.ts +11 -0
- package/src/universal/Picker/types.ts +79 -0
- package/src/universal/index.ts +4 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7-sources.jar.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7-sources.jar.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7-sources.jar.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7-sources.jar.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7.aar +0 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7.aar.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7.aar.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7.aar.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7.aar.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7.module.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7.module.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7.module.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7.module.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7.pom.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7.pom.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7.pom.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.7/expo.modules.ui-56.0.7.pom.sha512 +0 -1
- package/src/community/bottom-sheet/CLAUDE.md +0 -55
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { MenuAction, MenuComponentProps, MenuComponentRef, NativeActionEvent } from './types';
|
|
4
|
+
import { Button } from '../../swift-ui/Button';
|
|
5
|
+
import { ContextMenu } from '../../swift-ui/ContextMenu';
|
|
6
|
+
import { Host } from '../../swift-ui/Host';
|
|
7
|
+
import { Menu } from '../../swift-ui/Menu';
|
|
8
|
+
import { RNHostView } from '../../swift-ui/RNHostView';
|
|
9
|
+
import { Section } from '../../swift-ui/Section';
|
|
10
|
+
import { Toggle } from '../../swift-ui/Toggle';
|
|
11
|
+
import {
|
|
12
|
+
disabled as disabledModifier,
|
|
13
|
+
foregroundColor as foregroundColorModifier,
|
|
14
|
+
tint as tintModifier,
|
|
15
|
+
} from '../../swift-ui/modifiers';
|
|
16
|
+
import type { ModifierConfig } from '../../types';
|
|
17
|
+
|
|
18
|
+
function actionId(action: MenuAction): string {
|
|
19
|
+
return action.id ?? action.title;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function makeEvent(action: MenuAction): NativeActionEvent {
|
|
23
|
+
return { nativeEvent: { event: actionId(action) } };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function renderAction(
|
|
27
|
+
action: MenuAction,
|
|
28
|
+
onPressAction: MenuComponentProps['onPressAction']
|
|
29
|
+
): React.ReactNode {
|
|
30
|
+
if (action.attributes?.hidden) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { subactions, displayInline, state, attributes, image, imageColor, title } = action;
|
|
35
|
+
const key = actionId(action);
|
|
36
|
+
const systemImage = typeof image === 'string' ? image : undefined;
|
|
37
|
+
// `tint` is what SwiftUI's `Menu`/`Button` honor for the leading SF Symbol —
|
|
38
|
+
// `foregroundColor` would also affect the label text.
|
|
39
|
+
const tintMod = imageColor ? tintModifier(imageColor) : null;
|
|
40
|
+
|
|
41
|
+
if (subactions && subactions.length > 0) {
|
|
42
|
+
const children = subactions.map((sub) => renderAction(sub, onPressAction));
|
|
43
|
+
if (displayInline) {
|
|
44
|
+
return (
|
|
45
|
+
<Section key={key} title={title}>
|
|
46
|
+
{children}
|
|
47
|
+
</Section>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
// SwiftUI's system menu UI ignores per-item color modifiers on submenu
|
|
51
|
+
// headers, so we don't forward `imageColor` here. Leaf `Button`s below
|
|
52
|
+
// tint via `foregroundColor`, which does take effect.
|
|
53
|
+
return (
|
|
54
|
+
<Menu key={key} label={title} systemImage={systemImage}>
|
|
55
|
+
{children}
|
|
56
|
+
</Menu>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const fire = () => onPressAction?.(makeEvent(action));
|
|
61
|
+
|
|
62
|
+
const modifiers: ModifierConfig[] = [];
|
|
63
|
+
if (attributes?.disabled) modifiers.push(disabledModifier(true));
|
|
64
|
+
|
|
65
|
+
if (state === 'on' || state === 'off') {
|
|
66
|
+
if (tintMod) modifiers.push(tintMod);
|
|
67
|
+
return (
|
|
68
|
+
<Toggle
|
|
69
|
+
key={key}
|
|
70
|
+
label={title}
|
|
71
|
+
systemImage={systemImage}
|
|
72
|
+
isOn={state === 'on'}
|
|
73
|
+
onIsOnChange={fire}
|
|
74
|
+
modifiers={modifiers.length > 0 ? modifiers : undefined}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// For a leaf `Button`, `foregroundColor` also tints the system image —
|
|
80
|
+
// upstream uses this to color both the label and the icon. Skip when the
|
|
81
|
+
// role is destructive so SwiftUI's red tint remains in effect.
|
|
82
|
+
if (imageColor && !attributes?.destructive) {
|
|
83
|
+
modifiers.push(foregroundColorModifier(imageColor));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Button
|
|
88
|
+
key={key}
|
|
89
|
+
label={title}
|
|
90
|
+
systemImage={systemImage}
|
|
91
|
+
role={attributes?.destructive ? 'destructive' : undefined}
|
|
92
|
+
modifiers={modifiers.length > 0 ? modifiers : undefined}
|
|
93
|
+
onPress={fire}
|
|
94
|
+
/>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let warnedShowNoop = false;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* A drop-in replacement for `@react-native-menu/menu` on iOS.
|
|
102
|
+
* Uses SwiftUI `Menu` for tap triggers and `ContextMenu` for long-press triggers.
|
|
103
|
+
*/
|
|
104
|
+
export function MenuView(props: MenuComponentProps & { ref?: React.Ref<MenuComponentRef> }) {
|
|
105
|
+
const { ref, actions, onPressAction, shouldOpenOnLongPress, title, style, children, testID } =
|
|
106
|
+
props;
|
|
107
|
+
|
|
108
|
+
// SwiftUI `Menu`/`ContextMenu` expose no programmatic open API, so `show()`
|
|
109
|
+
// can't do anything on iOS. Surface that as a one-time dev warning instead of
|
|
110
|
+
// silently no-opping.
|
|
111
|
+
React.useImperativeHandle(
|
|
112
|
+
ref,
|
|
113
|
+
() => ({
|
|
114
|
+
show: () => {
|
|
115
|
+
if (__DEV__ && !warnedShowNoop) {
|
|
116
|
+
warnedShowNoop = true;
|
|
117
|
+
console.warn(
|
|
118
|
+
'[@expo/ui] MenuView.show() is a no-op on iOS. SwiftUI Menu/ContextMenu have no programmatic open API.'
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
}),
|
|
123
|
+
[]
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const items = actions.map((action) => renderAction(action, onPressAction));
|
|
127
|
+
const body = title ? <Section title={title}>{items}</Section> : items;
|
|
128
|
+
|
|
129
|
+
// RNHostView requires a single ReactElement; wrap in a fragment so callers
|
|
130
|
+
// can pass any `ReactNode` (string, array, etc.).
|
|
131
|
+
const trigger = (
|
|
132
|
+
<RNHostView matchContents>
|
|
133
|
+
<>{children}</>
|
|
134
|
+
</RNHostView>
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<Host matchContents style={style} testID={testID}>
|
|
139
|
+
{shouldOpenOnLongPress ? (
|
|
140
|
+
<ContextMenu>
|
|
141
|
+
<ContextMenu.Trigger>{trigger}</ContextMenu.Trigger>
|
|
142
|
+
<ContextMenu.Items>{body}</ContextMenu.Items>
|
|
143
|
+
</ContextMenu>
|
|
144
|
+
) : (
|
|
145
|
+
<Menu label={trigger}>{body}</Menu>
|
|
146
|
+
)}
|
|
147
|
+
</Host>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Ref, useEffect, useImperativeHandle } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import type { MenuComponentProps, MenuComponentRef } from './types';
|
|
5
|
+
|
|
6
|
+
let warned = false;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A drop-in replacement for `@react-native-menu/menu`'s `MenuView`. Wrap any trigger
|
|
10
|
+
* view; long-pressing or tapping (per `shouldOpenOnLongPress`) shows a popup menu
|
|
11
|
+
* built from the `actions` tree.
|
|
12
|
+
*
|
|
13
|
+
* - On Android, renders via Compose's `DropdownMenu` anchored to a `Pressable`.
|
|
14
|
+
* - On iOS, renders via SwiftUI's `Menu` (tap) or `ContextMenu` (long-press).
|
|
15
|
+
* - On web, the trigger renders the trigger but actions do not fire;
|
|
16
|
+
* a one-time `console.warn` is emitted.
|
|
17
|
+
*
|
|
18
|
+
* @platform android
|
|
19
|
+
* @platform ios
|
|
20
|
+
*/
|
|
21
|
+
export function MenuView(props: MenuComponentProps & { ref?: Ref<MenuComponentRef> }) {
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!warned) {
|
|
24
|
+
warned = true;
|
|
25
|
+
console.warn(
|
|
26
|
+
"[@expo/ui] MenuView is currently Android and iOS-only; the trigger will render but actions won't fire on this platform."
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}, []);
|
|
30
|
+
useImperativeHandle(props.ref, () => ({ show: () => {} }), []);
|
|
31
|
+
return (
|
|
32
|
+
<View style={props.style} testID={props.testID}>
|
|
33
|
+
{props.children}
|
|
34
|
+
</View>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MenuView } from './MenuView';
|
|
2
|
+
|
|
3
|
+
export type {
|
|
4
|
+
MenuAction,
|
|
5
|
+
MenuAttributes,
|
|
6
|
+
MenuComponentProps,
|
|
7
|
+
MenuState,
|
|
8
|
+
MenuComponentRef,
|
|
9
|
+
NativeActionEvent,
|
|
10
|
+
} from './types';
|
|
11
|
+
|
|
12
|
+
export default MenuView;
|
|
13
|
+
// named export needed for docs generator
|
|
14
|
+
export { MenuView };
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { ColorValue, ImageSourcePropType, StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
import type { SFSymbol } from 'sf-symbols-typescript';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Visual and behavioral attributes of a menu action.
|
|
7
|
+
* Compatible with `@react-native-menu/menu`.
|
|
8
|
+
*/
|
|
9
|
+
export type MenuAttributes = {
|
|
10
|
+
/**
|
|
11
|
+
* Renders the action with a destructive style (red text/icon).
|
|
12
|
+
*/
|
|
13
|
+
destructive?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Disables the action so it can't be activated.
|
|
16
|
+
*/
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Hides the action from the menu.
|
|
20
|
+
*/
|
|
21
|
+
hidden?: boolean;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Selection state for a menu action.
|
|
26
|
+
* `'on'` renders a checkmark; `'off'` doesn't.
|
|
27
|
+
*/
|
|
28
|
+
export type MenuState = 'on' | 'off';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A single action inside a `MenuView`.
|
|
32
|
+
* Compatible with `@react-native-menu/menu`.
|
|
33
|
+
*/
|
|
34
|
+
export type MenuAction = {
|
|
35
|
+
/**
|
|
36
|
+
* Identifier passed back via `onPressAction.nativeEvent.event` when this action is selected.
|
|
37
|
+
* Defaults to `title` if omitted.
|
|
38
|
+
*/
|
|
39
|
+
id?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Action label shown in the menu.
|
|
42
|
+
*/
|
|
43
|
+
title: string;
|
|
44
|
+
/**
|
|
45
|
+
* Text color of the action label.
|
|
46
|
+
* @platform android
|
|
47
|
+
*/
|
|
48
|
+
titleColor?: ColorValue;
|
|
49
|
+
/**
|
|
50
|
+
* Icon to render beside the action label.
|
|
51
|
+
*
|
|
52
|
+
* - When an `SFSymbol` name (e.g. `'trash'`), rendered on iOS only.
|
|
53
|
+
* Not rendered on Android — pass an `ImageSourcePropType` instead to show an
|
|
54
|
+
* icon there.
|
|
55
|
+
* - When an `ImageSourcePropType` (e.g. `require('./trash.xml')` or
|
|
56
|
+
* `{ uri: '...' }`), rendered on Android via Compose `Icon`. Ignored on iOS;
|
|
57
|
+
* SwiftUI menus only accept SF Symbol names for built-in `Menu`/`Button`
|
|
58
|
+
* labels.
|
|
59
|
+
*/
|
|
60
|
+
image?: SFSymbol | ImageSourcePropType;
|
|
61
|
+
/**
|
|
62
|
+
* Tint color applied to the action's icon.
|
|
63
|
+
*
|
|
64
|
+
* Visually applied on Android via the leading `Icon`'s tint. On iOS, the
|
|
65
|
+
* value is accepted but **may not render**: SwiftUI's `Menu`/`ContextMenu`
|
|
66
|
+
* draw their items via the system menu UI, which ignores per-item color
|
|
67
|
+
* modifiers.
|
|
68
|
+
*/
|
|
69
|
+
imageColor?: ColorValue;
|
|
70
|
+
/**
|
|
71
|
+
* Selection state. When `'on'`, the action renders a checkmark.
|
|
72
|
+
*/
|
|
73
|
+
state?: MenuState;
|
|
74
|
+
/**
|
|
75
|
+
* Visual/behavioral flags.
|
|
76
|
+
*/
|
|
77
|
+
attributes?: MenuAttributes;
|
|
78
|
+
/**
|
|
79
|
+
* Nested actions. Without `displayInline`, renders as a submenu;
|
|
80
|
+
* with `displayInline: true`, renders as an inline section.
|
|
81
|
+
*/
|
|
82
|
+
subactions?: MenuAction[];
|
|
83
|
+
/**
|
|
84
|
+
* When `true` and `subactions` is present, renders the children as an inline section
|
|
85
|
+
* inside the parent menu (with this action's `title` as the section header on iOS).
|
|
86
|
+
*/
|
|
87
|
+
displayInline?: boolean;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Imperative handle exposed by `MenuView` via `ref`.
|
|
92
|
+
* Compatible with `@react-native-menu/menu`'s `ref.show()` API.
|
|
93
|
+
*/
|
|
94
|
+
export type MenuComponentRef = {
|
|
95
|
+
/**
|
|
96
|
+
* Programmatically open the menu.
|
|
97
|
+
*
|
|
98
|
+
* On Android, opens the anchored `DropdownMenu` (equivalent to the user tapping
|
|
99
|
+
* the trigger). On iOS this is a no-op — SwiftUI `Menu`/`ContextMenu` have no
|
|
100
|
+
* programmatic open API; a one-time `console.warn` is emitted in development.
|
|
101
|
+
* @platform android
|
|
102
|
+
*/
|
|
103
|
+
show: () => void;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Event payload delivered to `onPressAction` when an action is selected.
|
|
108
|
+
* Compatible with `@react-native-menu/menu`.
|
|
109
|
+
*/
|
|
110
|
+
export type NativeActionEvent = {
|
|
111
|
+
nativeEvent: {
|
|
112
|
+
/** Identifier of the pressed action: `action.id ?? action.title`. */
|
|
113
|
+
event: string;
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Props for the `MenuView` component.
|
|
119
|
+
* Drop-in compatible with `@react-native-menu/menu`.
|
|
120
|
+
*/
|
|
121
|
+
export type MenuComponentProps = {
|
|
122
|
+
/**
|
|
123
|
+
* Menu title shown at the top of the menu.
|
|
124
|
+
* @platform ios
|
|
125
|
+
*/
|
|
126
|
+
title?: string;
|
|
127
|
+
/**
|
|
128
|
+
* Callback invoked when a menu action is selected.
|
|
129
|
+
*/
|
|
130
|
+
onPressAction?: (event: NativeActionEvent) => void;
|
|
131
|
+
/**
|
|
132
|
+
* Callback invoked when the menu opens.
|
|
133
|
+
*
|
|
134
|
+
* On Android, fires when the trigger's tap/long-press flips `expanded` to `true`.
|
|
135
|
+
* On iOS, SwiftUI `Menu`/`ContextMenu` do not expose an open hook, so this is not
|
|
136
|
+
* fired there.
|
|
137
|
+
* @platform android
|
|
138
|
+
*/
|
|
139
|
+
onOpenMenu?: () => void;
|
|
140
|
+
/**
|
|
141
|
+
* Callback invoked when the menu closes (either via dismissal or after an action
|
|
142
|
+
* fires).
|
|
143
|
+
*
|
|
144
|
+
* On Android, fires from the controlled `DropdownMenu`'s dismiss path.
|
|
145
|
+
* On iOS, SwiftUI `Menu`/`ContextMenu` do not expose a close hook in a way we can
|
|
146
|
+
* forward, so this is not fired there.
|
|
147
|
+
* @platform android
|
|
148
|
+
*/
|
|
149
|
+
onCloseMenu?: () => void;
|
|
150
|
+
/**
|
|
151
|
+
* The actions to display in the menu.
|
|
152
|
+
*/
|
|
153
|
+
actions: MenuAction[];
|
|
154
|
+
/**
|
|
155
|
+
* When `true`, the menu opens on long-press of the trigger instead of a single tap.
|
|
156
|
+
* @default false
|
|
157
|
+
*/
|
|
158
|
+
shouldOpenOnLongPress?: boolean;
|
|
159
|
+
/**
|
|
160
|
+
* Style applied to the trigger wrapper.
|
|
161
|
+
*/
|
|
162
|
+
style?: StyleProp<ViewStyle>;
|
|
163
|
+
/**
|
|
164
|
+
* Test identifier passed through to the trigger view.
|
|
165
|
+
*/
|
|
166
|
+
testID?: string;
|
|
167
|
+
/**
|
|
168
|
+
* Trigger view. Long-pressing or tapping (per `shouldOpenOnLongPress`) opens the menu.
|
|
169
|
+
*/
|
|
170
|
+
children?: ReactNode;
|
|
171
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { requireNativeView } from 'expo';
|
|
2
|
+
import { type ColorValue } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import type { ObservableState } from '../../State/useNativeState';
|
|
5
|
+
import { getStateId } from '../../State/utils';
|
|
6
|
+
import { type ModifierConfig } from '../../types';
|
|
7
|
+
import { createViewModifierEventListener } from '../modifiers/utils';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Common props shared by loading indicator variants.
|
|
11
|
+
*/
|
|
12
|
+
export type LoadingIndicatorCommonConfig = {
|
|
13
|
+
/**
|
|
14
|
+
* An observable state that holds the current progress value.
|
|
15
|
+
* Create one with `useNativeState(0)`. Omit for indeterminate loading.
|
|
16
|
+
*/
|
|
17
|
+
progress?: ObservableState<number | null>;
|
|
18
|
+
/**
|
|
19
|
+
* Loading indicator color.
|
|
20
|
+
*/
|
|
21
|
+
color?: ColorValue;
|
|
22
|
+
/**
|
|
23
|
+
* Modifiers for the component.
|
|
24
|
+
*/
|
|
25
|
+
modifiers?: ModifierConfig[];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type NativeLoadingIndicatorCommonConfig = Omit<
|
|
29
|
+
LoadingIndicatorCommonConfig,
|
|
30
|
+
'progress' | 'modifiers'
|
|
31
|
+
> & {
|
|
32
|
+
progress?: number;
|
|
33
|
+
modifiers?: unknown;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function transformProps<T extends LoadingIndicatorCommonConfig>(
|
|
37
|
+
props: T
|
|
38
|
+
): NativeLoadingIndicatorCommonConfig {
|
|
39
|
+
const { modifiers, progress, ...restProps } = props;
|
|
40
|
+
return {
|
|
41
|
+
modifiers,
|
|
42
|
+
...(modifiers ? createViewModifierEventListener(modifiers) : undefined),
|
|
43
|
+
...restProps,
|
|
44
|
+
progress: getStateId(progress),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function createLoadingIndicatorComponent<P extends LoadingIndicatorCommonConfig>(
|
|
49
|
+
viewName: string
|
|
50
|
+
): React.ComponentType<P> {
|
|
51
|
+
const NativeView: React.ComponentType<NativeLoadingIndicatorCommonConfig> = requireNativeView(
|
|
52
|
+
'ExpoUI',
|
|
53
|
+
viewName
|
|
54
|
+
);
|
|
55
|
+
function Component(props: P) {
|
|
56
|
+
return <NativeView {...transformProps(props)} />;
|
|
57
|
+
}
|
|
58
|
+
Component.displayName = viewName;
|
|
59
|
+
return Component;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// region LoadingIndicator
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* A loading indicator that displays loading using morphing shapes.
|
|
66
|
+
*
|
|
67
|
+
* Matches the Jetpack Compose `LoadingIndicator`.
|
|
68
|
+
*/
|
|
69
|
+
export const LoadingIndicator = createLoadingIndicatorComponent('LoadingIndicatorView');
|
|
70
|
+
|
|
71
|
+
// endregion
|
|
72
|
+
|
|
73
|
+
// region ContainedLoadingIndicator
|
|
74
|
+
|
|
75
|
+
export type ContainedLoadingIndicatorProps = LoadingIndicatorCommonConfig & {
|
|
76
|
+
/**
|
|
77
|
+
* Loading indicator's container color
|
|
78
|
+
*/
|
|
79
|
+
containerColor?: ColorValue;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* A loading indicator that displays loading using morphing shapes inside a container.
|
|
84
|
+
*
|
|
85
|
+
* Matches the Jetpack Compose `ContainedLoadingIndicator`.
|
|
86
|
+
*/
|
|
87
|
+
export const ContainedLoadingIndicator =
|
|
88
|
+
createLoadingIndicatorComponent<ContainedLoadingIndicatorProps>('ContainedLoadingIndicatorView');
|
|
89
|
+
// endregion
|
|
90
|
+
|
|
91
|
+
// Exported for docs api data
|
|
92
|
+
export { type ObservableState };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { requireNativeView } from 'expo';
|
|
2
|
+
import { type Ref } from 'react';
|
|
3
|
+
import { type ColorValue } from 'react-native';
|
|
4
|
+
|
|
5
|
+
import { type ModifierConfig } from '../../types';
|
|
6
|
+
import { createViewModifierEventListener } from '../modifiers/utils';
|
|
7
|
+
|
|
8
|
+
export type SnackbarProps = {
|
|
9
|
+
/**
|
|
10
|
+
* The background color of the snackbar container.
|
|
11
|
+
*/
|
|
12
|
+
containerColor?: ColorValue;
|
|
13
|
+
/**
|
|
14
|
+
* The preferred content color used for the message text.
|
|
15
|
+
*/
|
|
16
|
+
contentColor?: ColorValue;
|
|
17
|
+
/**
|
|
18
|
+
* The content color used for the action button.
|
|
19
|
+
*/
|
|
20
|
+
actionContentColor?: ColorValue;
|
|
21
|
+
/**
|
|
22
|
+
* The content color used for the dismiss-action icon button.
|
|
23
|
+
*/
|
|
24
|
+
dismissActionContentColor?: ColorValue;
|
|
25
|
+
/**
|
|
26
|
+
* Whether the action should be placed on a new line below the message.
|
|
27
|
+
* Useful for long action labels.
|
|
28
|
+
* @default false
|
|
29
|
+
*/
|
|
30
|
+
actionOnNewLine?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Modifiers for the component.
|
|
33
|
+
*/
|
|
34
|
+
modifiers?: ModifierConfig[];
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const SnackbarNativeView: React.ComponentType<SnackbarProps> = requireNativeView(
|
|
38
|
+
'ExpoUI',
|
|
39
|
+
'SnackbarView'
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Styling configuration for the snackbar shown by `SnackbarHost`. Pass as a
|
|
44
|
+
* child to override colors or place the action on a new line.
|
|
45
|
+
*/
|
|
46
|
+
export function Snackbar(props: SnackbarProps) {
|
|
47
|
+
const { modifiers, ...rest } = props;
|
|
48
|
+
return (
|
|
49
|
+
<SnackbarNativeView
|
|
50
|
+
modifiers={modifiers}
|
|
51
|
+
{...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
|
|
52
|
+
{...rest}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// --- SnackbarHost ---
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* How long the snackbar is shown. Mirrors Compose's `SnackbarDuration` enum.
|
|
61
|
+
*/
|
|
62
|
+
export type SnackbarDuration = 'short' | 'long' | 'indefinite';
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Reason a snackbar invocation resolved. Mirrors Compose's `SnackbarResult` enum.
|
|
66
|
+
*/
|
|
67
|
+
export type SnackbarResult = 'actionPerformed' | 'dismissed';
|
|
68
|
+
|
|
69
|
+
export type SnackbarShowOptions = {
|
|
70
|
+
/**
|
|
71
|
+
* The message body of the snackbar.
|
|
72
|
+
*/
|
|
73
|
+
message: string;
|
|
74
|
+
/**
|
|
75
|
+
* Label for the optional action button. When omitted, no action button is shown.
|
|
76
|
+
*/
|
|
77
|
+
actionLabel?: string;
|
|
78
|
+
/**
|
|
79
|
+
* Whether to show a trailing close (X) icon button to dismiss the snackbar.
|
|
80
|
+
* @default false
|
|
81
|
+
*/
|
|
82
|
+
withDismissAction?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* How long to show the snackbar. Defaults to `'short'` when an `actionLabel`
|
|
85
|
+
* is not provided, and `'indefinite'` when it is, matching Compose.
|
|
86
|
+
*/
|
|
87
|
+
duration?: SnackbarDuration;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export type SnackbarHostRef = {
|
|
91
|
+
/**
|
|
92
|
+
* Shows a snackbar and resolves with `'actionPerformed'` when the user taps
|
|
93
|
+
* the action, or `'dismissed'` when it times out or the dismiss-action
|
|
94
|
+
* button is tapped. Subsequent calls queue and show after the current
|
|
95
|
+
* snackbar is dismissed.
|
|
96
|
+
*/
|
|
97
|
+
showSnackbar: (options: SnackbarShowOptions) => Promise<SnackbarResult>;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export type SnackbarHostProps = {
|
|
101
|
+
/**
|
|
102
|
+
* Ref exposing the imperative `showSnackbar` method.
|
|
103
|
+
*/
|
|
104
|
+
ref?: Ref<SnackbarHostRef>;
|
|
105
|
+
/**
|
|
106
|
+
* Modifiers for the component.
|
|
107
|
+
*/
|
|
108
|
+
modifiers?: ModifierConfig[];
|
|
109
|
+
/**
|
|
110
|
+
* Optional `Snackbar` child supplying styling for shown snackbars. Mirrors
|
|
111
|
+
* Compose's `SnackbarHost(hostState) { data -> Snackbar(data, ...) }` lambda.
|
|
112
|
+
*/
|
|
113
|
+
children?: React.ReactNode;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const SnackbarHostNativeView: React.ComponentType<SnackbarHostProps> = requireNativeView(
|
|
117
|
+
'ExpoUI',
|
|
118
|
+
'SnackbarHostView'
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* A Material 3 [SnackbarHost](https://developer.android.com/develop/ui/compose/components/snackbar)
|
|
123
|
+
* that displays snackbars triggered via its ref's `showSnackbar` method.
|
|
124
|
+
*/
|
|
125
|
+
export function SnackbarHost(props: SnackbarHostProps) {
|
|
126
|
+
const { modifiers, children, ...rest } = props;
|
|
127
|
+
return (
|
|
128
|
+
<SnackbarHostNativeView
|
|
129
|
+
modifiers={modifiers}
|
|
130
|
+
{...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
|
|
131
|
+
{...rest}>
|
|
132
|
+
{children}
|
|
133
|
+
</SnackbarHostNativeView>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
@@ -49,6 +49,7 @@ export {
|
|
|
49
49
|
type HorizontalPagerProps,
|
|
50
50
|
} from './HorizontalPager';
|
|
51
51
|
export * from './SearchBar';
|
|
52
|
+
export * from './Snackbar';
|
|
52
53
|
export * from './DockedSearchBar';
|
|
53
54
|
export * from './HorizontalFloatingToolbar';
|
|
54
55
|
export * from './FloatingActionButton';
|
|
@@ -57,6 +58,7 @@ export * from './RadioButton';
|
|
|
57
58
|
export * from './Surface';
|
|
58
59
|
export { type TextProps, Text } from './Text';
|
|
59
60
|
export * from './Tooltip';
|
|
61
|
+
export * from './LoadingIndicator';
|
|
60
62
|
|
|
61
63
|
export * from './AnimatedVisibility';
|
|
62
64
|
export * from './Box';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ColorValue } from 'react-native';
|
|
2
2
|
|
|
3
|
-
import { type AnimatedValue } from './animation';
|
|
3
|
+
import { type AnimatedValue, type AnimationSpec } from './animation';
|
|
4
4
|
import { createModifier, createModifierWithEventListener } from './createModifier';
|
|
5
5
|
export { type ExpoModifier, type ModifierConfig } from '../../types';
|
|
6
6
|
export {
|
|
@@ -142,9 +142,12 @@ export const offset = (x: number, y: number) => createModifier('offset', { x, y
|
|
|
142
142
|
|
|
143
143
|
/**
|
|
144
144
|
* Sets the background color.
|
|
145
|
+
* Pass an `animationSpec` to smoothly animate between colors when the prop changes (backed by `animateColorAsState`).
|
|
145
146
|
* @param color - A color string (hex, e.g., `'#FF0000'`).
|
|
147
|
+
* @param options.animationSpec - Optional spec — animate between color changes.
|
|
146
148
|
*/
|
|
147
|
-
export const background = (color: ColorValue
|
|
149
|
+
export const background = (color: ColorValue, options?: { animationSpec?: AnimationSpec }) =>
|
|
150
|
+
createModifier('background', { color, animationSpec: options?.animationSpec });
|
|
148
151
|
|
|
149
152
|
/**
|
|
150
153
|
* Adds a border around the view.
|
|
@@ -291,6 +294,31 @@ export const clickable = (handler: () => void, options?: { indication?: boolean
|
|
|
291
294
|
indication: options?.indication ?? true,
|
|
292
295
|
});
|
|
293
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Makes the view respond to both click and long-click gestures.
|
|
299
|
+
* Wraps Compose's `Modifier.combinedClickable`. Useful for triggering a `DropdownMenu`
|
|
300
|
+
* on long-press while keeping a separate short-press action.
|
|
301
|
+
* @param handlers.onClick - Function to call on a short tap.
|
|
302
|
+
* @param handlers.onLongClick - Function to call on a long press.
|
|
303
|
+
* @param options - Optional configuration.
|
|
304
|
+
* @param options.indication - Whether to show a ripple indication. Defaults to `true`.
|
|
305
|
+
*/
|
|
306
|
+
export const combinedClickable = (
|
|
307
|
+
handlers: { onClick?: () => void; onLongClick?: () => void },
|
|
308
|
+
options?: { indication?: boolean }
|
|
309
|
+
) =>
|
|
310
|
+
createModifierWithEventListener(
|
|
311
|
+
'combinedClickable',
|
|
312
|
+
(params: { event: 'click' | 'longClick' }) => {
|
|
313
|
+
if (params.event === 'click') {
|
|
314
|
+
handlers.onClick?.();
|
|
315
|
+
} else if (params.event === 'longClick') {
|
|
316
|
+
handlers.onLongClick?.();
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
{ indication: options?.indication ?? true }
|
|
320
|
+
);
|
|
321
|
+
|
|
294
322
|
/**
|
|
295
323
|
* Makes the view selectable, like a radio button row.
|
|
296
324
|
* @param selected - Whether the item is currently selected.
|