@mrmeg/expo-ui 0.1.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.
Files changed (112) hide show
  1. package/README.md +96 -0
  2. package/dist/components/Accordion.d.ts +54 -0
  3. package/dist/components/Accordion.js +149 -0
  4. package/dist/components/Alert.d.ts +30 -0
  5. package/dist/components/Alert.js +25 -0
  6. package/dist/components/AnimatedView.d.ts +55 -0
  7. package/dist/components/AnimatedView.js +39 -0
  8. package/dist/components/Badge.d.ts +23 -0
  9. package/dist/components/Badge.js +74 -0
  10. package/dist/components/BottomSheet.d.ts +74 -0
  11. package/dist/components/BottomSheet.js +513 -0
  12. package/dist/components/Button.d.ts +129 -0
  13. package/dist/components/Button.js +216 -0
  14. package/dist/components/Card.d.ts +42 -0
  15. package/dist/components/Card.js +126 -0
  16. package/dist/components/Checkbox.d.ts +39 -0
  17. package/dist/components/Checkbox.js +96 -0
  18. package/dist/components/Collapsible.d.ts +67 -0
  19. package/dist/components/Collapsible.js +38 -0
  20. package/dist/components/Dialog.d.ts +140 -0
  21. package/dist/components/Dialog.js +167 -0
  22. package/dist/components/DismissKeyboard.d.ts +15 -0
  23. package/dist/components/DismissKeyboard.js +13 -0
  24. package/dist/components/Drawer.d.ts +74 -0
  25. package/dist/components/Drawer.js +423 -0
  26. package/dist/components/DropdownMenu.d.ts +120 -0
  27. package/dist/components/DropdownMenu.js +211 -0
  28. package/dist/components/EmptyState.d.ts +42 -0
  29. package/dist/components/EmptyState.js +58 -0
  30. package/dist/components/ErrorBoundary.d.ts +53 -0
  31. package/dist/components/ErrorBoundary.js +75 -0
  32. package/dist/components/Icon.d.ts +46 -0
  33. package/dist/components/Icon.js +40 -0
  34. package/dist/components/InputOTP.d.ts +72 -0
  35. package/dist/components/InputOTP.js +155 -0
  36. package/dist/components/Label.d.ts +61 -0
  37. package/dist/components/Label.js +72 -0
  38. package/dist/components/MaxWidthContainer.d.ts +58 -0
  39. package/dist/components/MaxWidthContainer.js +64 -0
  40. package/dist/components/Notification.d.ts +26 -0
  41. package/dist/components/Notification.js +230 -0
  42. package/dist/components/Popover.d.ts +79 -0
  43. package/dist/components/Popover.js +91 -0
  44. package/dist/components/Progress.d.ts +28 -0
  45. package/dist/components/Progress.js +107 -0
  46. package/dist/components/RadioGroup.d.ts +65 -0
  47. package/dist/components/RadioGroup.js +142 -0
  48. package/dist/components/Select.d.ts +88 -0
  49. package/dist/components/Select.js +172 -0
  50. package/dist/components/Separator.d.ts +83 -0
  51. package/dist/components/Separator.js +85 -0
  52. package/dist/components/Skeleton.d.ts +68 -0
  53. package/dist/components/Skeleton.js +99 -0
  54. package/dist/components/Slider.d.ts +24 -0
  55. package/dist/components/Slider.js +162 -0
  56. package/dist/components/StatusBar.d.ts +1 -0
  57. package/dist/components/StatusBar.js +19 -0
  58. package/dist/components/StyledText.d.ts +161 -0
  59. package/dist/components/StyledText.js +193 -0
  60. package/dist/components/Switch.d.ts +44 -0
  61. package/dist/components/Switch.js +129 -0
  62. package/dist/components/Tabs.d.ts +31 -0
  63. package/dist/components/Tabs.js +127 -0
  64. package/dist/components/TextInput.d.ts +120 -0
  65. package/dist/components/TextInput.js +263 -0
  66. package/dist/components/Toggle.d.ts +106 -0
  67. package/dist/components/Toggle.js +150 -0
  68. package/dist/components/ToggleGroup.d.ts +80 -0
  69. package/dist/components/ToggleGroup.js +189 -0
  70. package/dist/components/Tooltip.d.ts +121 -0
  71. package/dist/components/Tooltip.js +132 -0
  72. package/dist/components/index.d.ts +35 -0
  73. package/dist/components/index.js +35 -0
  74. package/dist/constants/colors.d.ts +82 -0
  75. package/dist/constants/colors.js +116 -0
  76. package/dist/constants/fonts.d.ts +32 -0
  77. package/dist/constants/fonts.js +91 -0
  78. package/dist/constants/index.d.ts +3 -0
  79. package/dist/constants/index.js +3 -0
  80. package/dist/constants/spacing.d.ts +40 -0
  81. package/dist/constants/spacing.js +48 -0
  82. package/dist/hooks/index.d.ts +6 -0
  83. package/dist/hooks/index.js +6 -0
  84. package/dist/hooks/useDimensions.d.ts +19 -0
  85. package/dist/hooks/useDimensions.js +55 -0
  86. package/dist/hooks/useReduceMotion.d.ts +5 -0
  87. package/dist/hooks/useReduceMotion.js +64 -0
  88. package/dist/hooks/useResources.d.ts +12 -0
  89. package/dist/hooks/useResources.js +56 -0
  90. package/dist/hooks/useScalePress.d.ts +57 -0
  91. package/dist/hooks/useScalePress.js +55 -0
  92. package/dist/hooks/useStaggeredEntrance.d.ts +67 -0
  93. package/dist/hooks/useStaggeredEntrance.js +74 -0
  94. package/dist/hooks/useTheme.d.ts +88 -0
  95. package/dist/hooks/useTheme.js +328 -0
  96. package/dist/index.d.ts +5 -0
  97. package/dist/index.js +5 -0
  98. package/dist/lib/animations.d.ts +1 -0
  99. package/dist/lib/animations.js +3 -0
  100. package/dist/lib/haptics.d.ts +3 -0
  101. package/dist/lib/haptics.js +29 -0
  102. package/dist/lib/index.d.ts +3 -0
  103. package/dist/lib/index.js +3 -0
  104. package/dist/lib/sentry.d.ts +16 -0
  105. package/dist/lib/sentry.js +55 -0
  106. package/dist/state/globalUIStore.d.ts +30 -0
  107. package/dist/state/globalUIStore.js +8 -0
  108. package/dist/state/index.d.ts +2 -0
  109. package/dist/state/index.js +2 -0
  110. package/dist/state/themeStore.d.ts +6 -0
  111. package/dist/state/themeStore.js +38 -0
  112. package/package.json +92 -0
package/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # @mrmeg/expo-ui
2
+
3
+ Reusable Expo and React Native UI primitives shared by the template and consumer apps. The package does not ship font files; web consumers load Lato from Google Fonts and native consumers use platform sans-serif fallbacks.
4
+
5
+ ## Install
6
+
7
+ Install from npm after publishing:
8
+
9
+ ```sh
10
+ bun add @mrmeg/expo-ui
11
+ ```
12
+
13
+ Consumers must also install the peer dependencies listed in `package.json`. Keep npm auth tokens in developer or CI configuration, not in this repository.
14
+
15
+ ## Imports
16
+
17
+ ```tsx
18
+ import { Button, StyledText } from "@mrmeg/expo-ui/components";
19
+ import { Button as ButtonDirect } from "@mrmeg/expo-ui/components/Button";
20
+ import { colors, spacing, typography } from "@mrmeg/expo-ui/constants";
21
+ import { useResources, useTheme } from "@mrmeg/expo-ui/hooks";
22
+ import { globalUIStore, useThemeStore } from "@mrmeg/expo-ui/state";
23
+ import { hapticLight, setupSentry } from "@mrmeg/expo-ui/lib";
24
+ ```
25
+
26
+ The root barrel also exports the public surface:
27
+
28
+ ```tsx
29
+ import { Button, colors, useTheme } from "@mrmeg/expo-ui";
30
+ ```
31
+
32
+ ## App Startup
33
+
34
+ Call `useResources()` once near the Expo app root before hiding the splash screen:
35
+
36
+ ```tsx
37
+ import { ThemeProvider } from "@react-navigation/native";
38
+ import { colors } from "@mrmeg/expo-ui/constants";
39
+ import { useResources, useTheme } from "@mrmeg/expo-ui/hooks";
40
+ import { Notification, StatusBar } from "@mrmeg/expo-ui/components";
41
+ import { PortalHost } from "@rn-primitives/portal";
42
+
43
+ export default function RootLayout() {
44
+ const { scheme } = useTheme();
45
+ const { loaded } = useResources();
46
+
47
+ if (!loaded) return null;
48
+
49
+ return (
50
+ <ThemeProvider
51
+ value={{
52
+ dark: colors[scheme ?? "light"].dark,
53
+ colors: colors[scheme ?? "light"].navigation,
54
+ fonts: colors[scheme ?? "light"].fonts,
55
+ }}
56
+ >
57
+ {/* App navigation goes here. */}
58
+ <Notification />
59
+ <PortalHost />
60
+ <StatusBar />
61
+ </ThemeProvider>
62
+ );
63
+ }
64
+ ```
65
+
66
+ ## Fonts
67
+
68
+ This package does not ship Lato `.ttf` files or other font binaries.
69
+
70
+ On web, `useResources()` injects the Google Fonts Lato stylesheet after hydration if the app has not already added it. For better first paint in Expo Router web apps, add the links in app-owned `app/+html.tsx`:
71
+
72
+ ```tsx
73
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
74
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
75
+ <link
76
+ id="mrmeg-expo-ui-lato"
77
+ rel="stylesheet"
78
+ href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap"
79
+ />
80
+ ```
81
+
82
+ On native, the package uses platform sans-serif fallbacks. `useResources()` still loads `Feather.font` from the consumer app's `@expo/vector-icons` peer dependency for icon rendering.
83
+
84
+ ## Package Checks
85
+
86
+ Run these before publishing:
87
+
88
+ ```sh
89
+ bun run ui:typecheck
90
+ bun run ui:test
91
+ bun run ui:build
92
+ bun run ui:pack
93
+ bun run ui:consumer-smoke
94
+ ```
95
+
96
+ `bun run ui:pack` runs a dry pack so the published file list and package size can be inspected before release.
@@ -0,0 +1,54 @@
1
+ import { View, ViewStyle } from "react-native";
2
+ import * as AccordionPrimitive from "@rn-primitives/accordion";
3
+ type BaseAccordionRootProps = Omit<React.ComponentProps<typeof View>, "style"> & React.RefAttributes<AccordionPrimitive.RootRef> & {
4
+ disabled?: boolean;
5
+ collapsible?: boolean;
6
+ dir?: "ltr" | "rtl";
7
+ orientation?: "vertical" | "horizontal";
8
+ style?: ViewStyle;
9
+ };
10
+ type WebSingleAccordionRootProps = BaseAccordionRootProps & {
11
+ type: "single";
12
+ defaultValue?: string;
13
+ value?: string;
14
+ onValueChange?: (value: string | undefined) => void;
15
+ };
16
+ type WebMultipleAccordionRootProps = BaseAccordionRootProps & {
17
+ type: "multiple";
18
+ defaultValue?: string[];
19
+ value?: string[];
20
+ onValueChange?: (value: string[]) => void;
21
+ };
22
+ type AccordionRootProps = WebSingleAccordionRootProps | WebMultipleAccordionRootProps;
23
+ /**
24
+ * Accordion Root Component
25
+ * Container for accordion items with support for single or multiple open items
26
+ *
27
+ * Usage:
28
+ * <Accordion type="single" collapsible>
29
+ * <AccordionItem value="item-1">
30
+ * <AccordionTrigger>Title</AccordionTrigger>
31
+ * <AccordionContent>Content</AccordionContent>
32
+ * </AccordionItem>
33
+ * </Accordion>
34
+ */
35
+ declare function Accordion({ children, style, ...props }: AccordionRootProps): import("react/jsx-runtime").JSX.Element;
36
+ /**
37
+ * Accordion Item Component
38
+ * Individual accordion item with border styling
39
+ */
40
+ declare function AccordionItem({ children, value, style: styleOverride, ...props }: AccordionPrimitive.ItemProps & React.RefAttributes<AccordionPrimitive.ItemRef>): import("react/jsx-runtime").JSX.Element;
41
+ /**
42
+ * Accordion Trigger Component
43
+ * Clickable header that expands/collapses the content
44
+ * Includes animated chevron icon
45
+ */
46
+ declare function AccordionTrigger({ children, style: styleOverride, ...props }: AccordionPrimitive.TriggerProps & {
47
+ children?: React.ReactNode;
48
+ } & React.RefAttributes<AccordionPrimitive.TriggerRef>): import("react/jsx-runtime").JSX.Element;
49
+ /**
50
+ * Accordion Content Component
51
+ * Expandable content area with animations
52
+ */
53
+ declare function AccordionContent({ children, style: styleOverride, ...props }: AccordionPrimitive.ContentProps & React.RefAttributes<AccordionPrimitive.ContentRef>): import("react/jsx-runtime").JSX.Element;
54
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
@@ -0,0 +1,149 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Platform, Pressable, View } from "react-native";
4
+ import Animated, { useSharedValue, useAnimatedStyle, withTiming, useReducedMotion, } from "react-native-reanimated";
5
+ import { Icon } from "./Icon";
6
+ import { TextClassContext } from "./StyledText";
7
+ import { useTheme } from "../hooks/useTheme";
8
+ import { spacing } from "../constants/spacing";
9
+ import * as AccordionPrimitive from "@rn-primitives/accordion";
10
+ function normalizeSingleValue(value) {
11
+ return value ?? "";
12
+ }
13
+ function denormalizeSingleValue(value) {
14
+ return value === "" ? undefined : value;
15
+ }
16
+ function normalizeMultipleValue(value) {
17
+ return value ?? [];
18
+ }
19
+ function WebSingleAccordionRoot(props) {
20
+ const isControlled = Object.prototype.hasOwnProperty.call(props, "value");
21
+ const [uncontrolledValue, setUncontrolledValue] = useState(() => normalizeSingleValue(props.defaultValue));
22
+ const normalizedValue = isControlled
23
+ ? normalizeSingleValue(props.value)
24
+ : uncontrolledValue;
25
+ const handleValueChange = (nextValue) => {
26
+ const normalizedNextValue = normalizeSingleValue(nextValue);
27
+ if (!isControlled) {
28
+ setUncontrolledValue(normalizedNextValue);
29
+ }
30
+ props.onValueChange?.(denormalizeSingleValue(normalizedNextValue));
31
+ };
32
+ const { children, style, type: _type, defaultValue: _defaultValue, onValueChange: _onValueChange, value: _value, ...rootProps } = props;
33
+ return (_jsx(AccordionPrimitive.Root, { ...rootProps, type: "single", value: normalizedValue, onValueChange: handleValueChange, asChild: false, children: _jsx(View, { style: style, children: children }) }));
34
+ }
35
+ function WebMultipleAccordionRoot(props) {
36
+ const isControlled = Object.prototype.hasOwnProperty.call(props, "value");
37
+ const [uncontrolledValue, setUncontrolledValue] = useState(() => normalizeMultipleValue(props.defaultValue));
38
+ const normalizedValue = isControlled
39
+ ? normalizeMultipleValue(props.value)
40
+ : uncontrolledValue;
41
+ const handleValueChange = (nextValue) => {
42
+ const normalizedNextValue = normalizeMultipleValue(nextValue);
43
+ if (!isControlled) {
44
+ setUncontrolledValue(normalizedNextValue);
45
+ }
46
+ props.onValueChange?.(normalizedNextValue);
47
+ };
48
+ const { children, style, type: _type, defaultValue: _defaultValue, onValueChange: _onValueChange, value: _value, ...rootProps } = props;
49
+ return (_jsx(AccordionPrimitive.Root, { ...rootProps, type: "multiple", value: normalizedValue, onValueChange: handleValueChange, asChild: false, children: _jsx(View, { style: style, children: children }) }));
50
+ }
51
+ function WebAccordionRoot(props) {
52
+ if (props.type === "multiple") {
53
+ return _jsx(WebMultipleAccordionRoot, { ...props });
54
+ }
55
+ return _jsx(WebSingleAccordionRoot, { ...props });
56
+ }
57
+ /**
58
+ * Accordion Root Component
59
+ * Container for accordion items with support for single or multiple open items
60
+ *
61
+ * Usage:
62
+ * <Accordion type="single" collapsible>
63
+ * <AccordionItem value="item-1">
64
+ * <AccordionTrigger>Title</AccordionTrigger>
65
+ * <AccordionContent>Content</AccordionContent>
66
+ * </AccordionItem>
67
+ * </Accordion>
68
+ */
69
+ function Accordion({ children, style, ...props }) {
70
+ if (Platform.OS === "web") {
71
+ return (_jsx(WebAccordionRoot, { ...props, style: style, children: children }));
72
+ }
73
+ return (_jsx(AccordionPrimitive.Root, { ...props, asChild: true, children: _jsx(View, { style: style, children: children }) }));
74
+ }
75
+ /**
76
+ * Accordion Item Component
77
+ * Individual accordion item with border styling
78
+ */
79
+ function AccordionItem({ children, value, style: styleOverride, ...props }) {
80
+ const { theme } = useTheme();
81
+ return (_jsx(AccordionPrimitive.Item, { value: value, asChild: true, ...props, children: _jsx(View, { style: [
82
+ {
83
+ borderBottomWidth: 1,
84
+ borderBottomColor: theme.colors.border,
85
+ overflow: "hidden",
86
+ },
87
+ // Spread array styles from primitives to prevent nested arrays on web
88
+ ...(styleOverride && typeof styleOverride !== "function"
89
+ ? (Array.isArray(styleOverride) ? styleOverride : [styleOverride])
90
+ : []),
91
+ ], children: children }) }));
92
+ }
93
+ const Trigger = Platform.OS === "web" ? View : Pressable;
94
+ /**
95
+ * Accordion Trigger Component
96
+ * Clickable header that expands/collapses the content
97
+ * Includes animated chevron icon
98
+ */
99
+ function AccordionTrigger({ children, style: styleOverride, ...props }) {
100
+ const { theme } = useTheme();
101
+ const reduceMotion = useReducedMotion();
102
+ const { isExpanded } = AccordionPrimitive.useItemContext();
103
+ const rotation = useSharedValue(isExpanded ? 1 : 0);
104
+ useEffect(() => {
105
+ const target = isExpanded ? 1 : 0;
106
+ if (reduceMotion) {
107
+ rotation.value = target;
108
+ return;
109
+ }
110
+ rotation.value = withTiming(target, {
111
+ duration: isExpanded ? 200 : 150,
112
+ });
113
+ }, [isExpanded, reduceMotion]);
114
+ const chevronStyle = useAnimatedStyle(() => ({
115
+ transform: [{ rotate: `${rotation.value * 180}deg` }],
116
+ }));
117
+ return (_jsx(TextClassContext.Provider, { value: "", children: _jsx(AccordionPrimitive.Header, { children: _jsx(AccordionPrimitive.Trigger, { ...props, asChild: true, children: _jsxs(Trigger, { style: [
118
+ {
119
+ flexDirection: "row",
120
+ alignItems: "center",
121
+ justifyContent: "space-between",
122
+ gap: spacing.md,
123
+ borderRadius: spacing.radiusMd,
124
+ paddingVertical: spacing.md,
125
+ ...(Platform.OS === "web" && { cursor: "pointer" }),
126
+ },
127
+ // Spread array styles from primitives to prevent nested arrays on web
128
+ ...(styleOverride && typeof styleOverride !== "function"
129
+ ? (Array.isArray(styleOverride) ? styleOverride : [styleOverride])
130
+ : []),
131
+ ], children: [_jsx(_Fragment, { children: children }), _jsx(Animated.View, { style: chevronStyle, children: _jsx(Icon, { name: "chevron-down", size: 16, color: theme.colors.textDim, decorative: true }) })] }) }) }) }));
132
+ }
133
+ /**
134
+ * Accordion Content Component
135
+ * Expandable content area with animations
136
+ */
137
+ function AccordionContent({ children, style: styleOverride, ...props }) {
138
+ return (_jsx(TextClassContext.Provider, { value: "", children: _jsx(AccordionPrimitive.Content, { ...props, children: _jsx(View, { style: [
139
+ {
140
+ paddingBottom: spacing.sm,
141
+ overflow: "hidden",
142
+ },
143
+ // Spread array styles from primitives to prevent nested arrays on web
144
+ ...(styleOverride && typeof styleOverride !== "function"
145
+ ? (Array.isArray(styleOverride) ? styleOverride : [styleOverride])
146
+ : []),
147
+ ], children: children }) }) }));
148
+ }
149
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Cross-platform Alert utility.
3
+ *
4
+ * Wraps React Native's Alert API to support both native and web platforms.
5
+ * On web, it uses `window.alert()` or `window.confirm()` to simulate native alerts.
6
+ *
7
+ * Usage:
8
+ * Alert.show({
9
+ * title: "Delete Item",
10
+ * message: "Are you sure you want to delete this?",
11
+ * buttons: [
12
+ * { text: "Cancel", style: "cancel" },
13
+ * { text: "Delete", style: "destructive", onPress: () => handleDelete() }
14
+ * ]
15
+ * });
16
+ */
17
+ type AlertButton = {
18
+ text: string;
19
+ onPress?: () => void;
20
+ style?: "default" | "cancel" | "destructive";
21
+ };
22
+ interface AlertParams {
23
+ title?: string;
24
+ message: string;
25
+ buttons?: AlertButton[];
26
+ }
27
+ export declare const Alert: {
28
+ show: ({ title, message, buttons }: AlertParams) => void;
29
+ };
30
+ export {};
@@ -0,0 +1,25 @@
1
+ import { Alert as RNAlert, Platform } from "react-native";
2
+ export const Alert = {
3
+ show: ({ title = "", message, buttons = [{ text: "OK" }] }) => {
4
+ if (Platform.OS === "web") {
5
+ if (buttons.length > 1) {
6
+ const result = window.confirm(`${title}\n${message}`);
7
+ const confirmButton = buttons.find(b => b.style !== "cancel");
8
+ const cancelButton = buttons.find(b => b.style === "cancel");
9
+ if (result && confirmButton?.onPress) {
10
+ confirmButton.onPress();
11
+ }
12
+ else if (!result && cancelButton?.onPress) {
13
+ cancelButton.onPress();
14
+ }
15
+ }
16
+ else {
17
+ window.alert(`${title}\n${message}`);
18
+ buttons[0]?.onPress?.();
19
+ }
20
+ }
21
+ else {
22
+ RNAlert.alert(title, message, buttons);
23
+ }
24
+ }
25
+ };
@@ -0,0 +1,55 @@
1
+ import React from "react";
2
+ import { ViewProps } from "react-native";
3
+ /**
4
+ * Animation type options
5
+ */
6
+ export type AnimationType = "fade" | "fadeSlideUp" | "fadeSlideDown" | "scale";
7
+ interface AnimatedViewProps extends ViewProps {
8
+ children: React.ReactNode;
9
+ /**
10
+ * Type of animation to use
11
+ * @default "fade"
12
+ */
13
+ type?: AnimationType;
14
+ /**
15
+ * Animation duration in milliseconds for enter animation
16
+ * @default 200
17
+ */
18
+ enterDuration?: number;
19
+ /**
20
+ * Delay before starting the enter animation (in milliseconds)
21
+ * Useful for staggered animations
22
+ * @default 0
23
+ */
24
+ delay?: number;
25
+ }
26
+ /**
27
+ * Cross-Platform Animated View Component
28
+ * Uses Reanimated for smooth 60fps animations on all platforms
29
+ *
30
+ * Features:
31
+ * - Multiple animation types (fade, fadeSlideUp, fadeSlideDown, scale)
32
+ * - Configurable enter duration
33
+ * - Optional delay for staggered animations
34
+ * - Respects reduced motion accessibility preference
35
+ *
36
+ * Usage:
37
+ * ```tsx
38
+ * // Simple fade
39
+ * <AnimatedView>
40
+ * {children}
41
+ * </AnimatedView>
42
+ *
43
+ * // Fade with slide up
44
+ * <AnimatedView type="fadeSlideUp">
45
+ * {children}
46
+ * </AnimatedView>
47
+ *
48
+ * // With delay (for staggered lists)
49
+ * <AnimatedView type="fadeSlideUp" delay={100}>
50
+ * {children}
51
+ * </AnimatedView>
52
+ * ```
53
+ */
54
+ export declare function AnimatedView({ children, type, enterDuration, delay, style, ...props }: AnimatedViewProps): import("react/jsx-runtime").JSX.Element;
55
+ export {};
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import Animated from "react-native-reanimated";
3
+ import { useStaggeredEntrance } from "../hooks/useStaggeredEntrance";
4
+ /**
5
+ * Cross-Platform Animated View Component
6
+ * Uses Reanimated for smooth 60fps animations on all platforms
7
+ *
8
+ * Features:
9
+ * - Multiple animation types (fade, fadeSlideUp, fadeSlideDown, scale)
10
+ * - Configurable enter duration
11
+ * - Optional delay for staggered animations
12
+ * - Respects reduced motion accessibility preference
13
+ *
14
+ * Usage:
15
+ * ```tsx
16
+ * // Simple fade
17
+ * <AnimatedView>
18
+ * {children}
19
+ * </AnimatedView>
20
+ *
21
+ * // Fade with slide up
22
+ * <AnimatedView type="fadeSlideUp">
23
+ * {children}
24
+ * </AnimatedView>
25
+ *
26
+ * // With delay (for staggered lists)
27
+ * <AnimatedView type="fadeSlideUp" delay={100}>
28
+ * {children}
29
+ * </AnimatedView>
30
+ * ```
31
+ */
32
+ export function AnimatedView({ children, type = "fade", enterDuration = 200, delay = 0, style, ...props }) {
33
+ const entranceStyle = useStaggeredEntrance({
34
+ type,
35
+ delay,
36
+ duration: enterDuration,
37
+ });
38
+ return (_jsx(Animated.View, { style: [style, entranceStyle], ...props, children: children }));
39
+ }
@@ -0,0 +1,23 @@
1
+ import React from "react";
2
+ import { StyleProp, ViewStyle } from "react-native";
3
+ export type BadgeVariant = "default" | "secondary" | "outline" | "destructive";
4
+ export interface BadgeProps {
5
+ children: React.ReactNode;
6
+ variant?: BadgeVariant;
7
+ style?: StyleProp<ViewStyle>;
8
+ }
9
+ /**
10
+ * Badge Component
11
+ *
12
+ * Small inline status label with pill shape.
13
+ *
14
+ * Usage:
15
+ * ```tsx
16
+ * <Badge>Default</Badge>
17
+ * <Badge variant="secondary">Secondary</Badge>
18
+ * <Badge variant="outline">Outline</Badge>
19
+ * <Badge variant="destructive">Error</Badge>
20
+ * ```
21
+ */
22
+ declare function Badge({ children, variant, style: styleOverride }: BadgeProps): import("react/jsx-runtime").JSX.Element;
23
+ export { Badge };
@@ -0,0 +1,74 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { View, StyleSheet } from "react-native";
4
+ import { useTheme } from "../hooks/useTheme";
5
+ import { spacing } from "../constants/spacing";
6
+ import { StyledText } from "./StyledText";
7
+ /**
8
+ * Badge Component
9
+ *
10
+ * Small inline status label with pill shape.
11
+ *
12
+ * Usage:
13
+ * ```tsx
14
+ * <Badge>Default</Badge>
15
+ * <Badge variant="secondary">Secondary</Badge>
16
+ * <Badge variant="outline">Outline</Badge>
17
+ * <Badge variant="destructive">Error</Badge>
18
+ * ```
19
+ */
20
+ function Badge({ children, variant = "default", style: styleOverride }) {
21
+ const { theme } = useTheme();
22
+ const styles = createStyles(theme);
23
+ const textStyle = [
24
+ styles.text,
25
+ variant === "default" && { color: theme.colors.primaryForeground },
26
+ variant === "secondary" && { color: theme.colors.secondaryForeground },
27
+ variant === "outline" && { color: theme.colors.foreground },
28
+ variant === "destructive" && { color: theme.colors.destructiveForeground },
29
+ ];
30
+ const normalizedChildren = React.Children.toArray(children);
31
+ const hasOnlyTextChildren = normalizedChildren.every((child) => typeof child === "string" || typeof child === "number");
32
+ const content = hasOnlyTextChildren ? (_jsx(StyledText, { style: textStyle, children: normalizedChildren.join("") })) : (React.Children.map(children, (child) => {
33
+ if (typeof child === "string" || typeof child === "number") {
34
+ return _jsx(StyledText, { style: textStyle, children: child });
35
+ }
36
+ return child;
37
+ }));
38
+ return (_jsx(View, { accessibilityRole: "text", style: [
39
+ styles.badge,
40
+ variant === "default" && styles.default,
41
+ variant === "secondary" && styles.secondary,
42
+ variant === "outline" && styles.outline,
43
+ variant === "destructive" && styles.destructive,
44
+ styleOverride,
45
+ ], children: content }));
46
+ }
47
+ const createStyles = (theme) => StyleSheet.create({
48
+ badge: {
49
+ alignSelf: "flex-start",
50
+ borderRadius: spacing.radiusFull,
51
+ paddingHorizontal: 10,
52
+ paddingVertical: 2,
53
+ },
54
+ default: {
55
+ backgroundColor: theme.colors.primary,
56
+ },
57
+ secondary: {
58
+ backgroundColor: theme.colors.secondary,
59
+ },
60
+ outline: {
61
+ backgroundColor: "transparent",
62
+ borderWidth: 1,
63
+ borderColor: theme.colors.border,
64
+ },
65
+ destructive: {
66
+ backgroundColor: theme.colors.destructive,
67
+ },
68
+ text: {
69
+ fontSize: 12,
70
+ fontWeight: "500",
71
+ lineHeight: 18,
72
+ },
73
+ });
74
+ export { Badge };
@@ -0,0 +1,74 @@
1
+ import React from "react";
2
+ import { ViewProps, StyleProp, ViewStyle, ScrollViewProps } from "react-native";
3
+ type SnapPoint = number | `${number}%`;
4
+ interface BottomSheetContextValue {
5
+ open: boolean;
6
+ onOpenChange: (open: boolean) => void;
7
+ toggle: () => void;
8
+ snapPoints: number[];
9
+ currentSnapIndex: number;
10
+ closeOnBackdropPress: boolean;
11
+ }
12
+ interface BottomSheetProps {
13
+ /** Controlled open state */
14
+ open?: boolean;
15
+ /** Callback when open state changes */
16
+ onOpenChange?: (open: boolean) => void;
17
+ /** Default open state for uncontrolled mode */
18
+ defaultOpen?: boolean;
19
+ /** Snap point heights (px or percentage strings) */
20
+ snapPoints?: SnapPoint[];
21
+ /** Whether to close when backdrop is pressed */
22
+ closeOnBackdropPress?: boolean;
23
+ children: React.ReactNode;
24
+ }
25
+ interface BottomSheetTriggerProps {
26
+ asChild?: boolean;
27
+ children: React.ReactNode;
28
+ style?: StyleProp<ViewStyle>;
29
+ }
30
+ interface BottomSheetContentProps extends ViewProps {
31
+ /** Whether to enable swipe/drag gestures */
32
+ swipeEnabled?: boolean;
33
+ /** Velocity threshold for quick swipe to close */
34
+ velocityThreshold?: number;
35
+ style?: StyleProp<ViewStyle>;
36
+ children: React.ReactNode;
37
+ }
38
+ interface BottomSheetHeaderProps extends ViewProps {
39
+ children: React.ReactNode;
40
+ }
41
+ interface BottomSheetBodyProps extends ScrollViewProps {
42
+ children: React.ReactNode;
43
+ }
44
+ interface BottomSheetFooterProps extends ViewProps {
45
+ children: React.ReactNode;
46
+ }
47
+ interface BottomSheetHandleProps {
48
+ style?: StyleProp<ViewStyle>;
49
+ }
50
+ interface BottomSheetCloseProps {
51
+ asChild?: boolean;
52
+ children: React.ReactNode;
53
+ style?: StyleProp<ViewStyle>;
54
+ }
55
+ declare function useBottomSheetContext(): BottomSheetContextValue;
56
+ declare function BottomSheetRoot({ open: controlledOpen, onOpenChange: controlledOnOpenChange, defaultOpen, snapPoints: rawSnapPoints, closeOnBackdropPress, children, }: BottomSheetProps): import("react/jsx-runtime").JSX.Element;
57
+ declare function BottomSheetTrigger({ asChild, children, style: styleOverride }: BottomSheetTriggerProps): import("react/jsx-runtime").JSX.Element;
58
+ declare function BottomSheetContent({ swipeEnabled, velocityThreshold, style: styleOverride, children, ...props }: BottomSheetContentProps): import("react/jsx-runtime").JSX.Element | null;
59
+ declare function BottomSheetHandle({ style }: BottomSheetHandleProps): import("react/jsx-runtime").JSX.Element;
60
+ declare function BottomSheetHeader({ children, style, ...props }: BottomSheetHeaderProps): import("react/jsx-runtime").JSX.Element;
61
+ declare function BottomSheetBody({ children, style, ...props }: BottomSheetBodyProps): import("react/jsx-runtime").JSX.Element;
62
+ declare function BottomSheetFooter({ children, style, ...props }: BottomSheetFooterProps): import("react/jsx-runtime").JSX.Element;
63
+ declare function BottomSheetClose({ asChild, children, style: styleOverride }: BottomSheetCloseProps): import("react/jsx-runtime").JSX.Element;
64
+ declare const BottomSheet: typeof BottomSheetRoot & {
65
+ Trigger: typeof BottomSheetTrigger;
66
+ Content: typeof BottomSheetContent;
67
+ Handle: typeof BottomSheetHandle;
68
+ Header: typeof BottomSheetHeader;
69
+ Body: typeof BottomSheetBody;
70
+ Footer: typeof BottomSheetFooter;
71
+ Close: typeof BottomSheetClose;
72
+ };
73
+ export { BottomSheet, BottomSheetTrigger, BottomSheetContent, BottomSheetHandle, BottomSheetHeader, BottomSheetBody, BottomSheetFooter, BottomSheetClose, useBottomSheetContext, };
74
+ export type { BottomSheetProps, BottomSheetTriggerProps, BottomSheetContentProps, BottomSheetHandleProps, BottomSheetHeaderProps, BottomSheetBodyProps, BottomSheetFooterProps, BottomSheetCloseProps, };