@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
@@ -0,0 +1,211 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Platform, StyleSheet, Text, View } from "react-native";
4
+ import { Icon } from "./Icon";
5
+ import { AnimatedView } from "./AnimatedView";
6
+ import { TextClassContext } from "./StyledText";
7
+ import { useTheme } from "../hooks/useTheme";
8
+ import { spacing } from "../constants/spacing";
9
+ import * as DropdownMenuPrimitive from "@rn-primitives/dropdown-menu";
10
+ import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens";
11
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
12
+ // Re-export primitives that don't need styling
13
+ const DropdownMenu = DropdownMenuPrimitive.Root;
14
+ const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
15
+ const DropdownMenuGroup = DropdownMenuPrimitive.Group;
16
+ const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
17
+ const DropdownMenuSub = DropdownMenuPrimitive.Sub;
18
+ const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
19
+ // Platform-specific overlay
20
+ const FullWindowOverlay = Platform.OS === "ios" ? RNFullWindowOverlay : React.Fragment;
21
+ function DropdownMenuSubTrigger({ inset = false, children, style: styleOverride, ...props }) {
22
+ const { theme } = useTheme();
23
+ const { open } = DropdownMenuPrimitive.useSubContext();
24
+ const iconName = Platform.OS === "web" ? "chevron-right" : open ? "chevron-up" : "chevron-down";
25
+ return (_jsx(TextClassContext.Provider, { value: "", children: _jsxs(DropdownMenuPrimitive.SubTrigger, { ...props, style: {
26
+ flexDirection: "row",
27
+ alignItems: "center",
28
+ borderRadius: spacing.radiusSm,
29
+ paddingHorizontal: spacing.sm,
30
+ paddingVertical: Platform.select({ web: spacing.xs, default: spacing.sm }),
31
+ backgroundColor: open ? theme.colors.card : "transparent",
32
+ ...(Platform.OS === "web" && {
33
+ cursor: "pointer",
34
+ outlineStyle: "none",
35
+ }),
36
+ ...(inset && { paddingLeft: spacing.xl }),
37
+ ...(styleOverride && typeof styleOverride !== "function"
38
+ ? StyleSheet.flatten(styleOverride)
39
+ : {}),
40
+ }, children: [typeof children === "function" ? null : children, _jsx(View, { style: { marginLeft: "auto" }, children: _jsx(Icon, { name: iconName, size: 16, color: theme.colors.text }) })] }) }));
41
+ }
42
+ function DropdownMenuSubContent({ style: styleOverride, ...props }) {
43
+ const { theme, getShadowStyle } = useTheme();
44
+ const shadowStyle = StyleSheet.flatten(getShadowStyle("soft"));
45
+ return (_jsx(AnimatedView, { type: "fade", children: _jsx(DropdownMenuPrimitive.SubContent, { ...props, style: {
46
+ backgroundColor: theme.colors.background,
47
+ borderWidth: 1,
48
+ borderColor: theme.colors.border,
49
+ borderRadius: spacing.radiusMd,
50
+ padding: spacing.xs,
51
+ minWidth: 192,
52
+ overflow: "hidden",
53
+ ...shadowStyle,
54
+ ...(Platform.OS === "web" && {
55
+ zIndex: 50,
56
+ }),
57
+ ...(styleOverride && typeof styleOverride !== "function"
58
+ ? StyleSheet.flatten(styleOverride)
59
+ : {}),
60
+ } }) }));
61
+ }
62
+ function DropdownMenuContent({ side, align = "start", sideOffset = 4, portalHost, style: styleOverride, ...props }) {
63
+ const { theme, getShadowStyle } = useTheme();
64
+ const shadowStyle = StyleSheet.flatten(getShadowStyle("soft"));
65
+ const insets = useSafeAreaInsets();
66
+ return (_jsx(DropdownMenuPrimitive.Portal, { hostName: portalHost, children: _jsx(FullWindowOverlay, { children: _jsx(DropdownMenuPrimitive.Overlay, { style: Platform.select({
67
+ native: StyleSheet.absoluteFill,
68
+ default: undefined,
69
+ }), children: _jsx(AnimatedView, { type: "fade", children: _jsx(TextClassContext.Provider, { value: "", children: _jsx(DropdownMenuPrimitive.Content, { side: side, align: align, sideOffset: sideOffset, insets: insets, avoidCollisions: true, ...props, style: {
70
+ backgroundColor: theme.colors.background,
71
+ borderWidth: 1,
72
+ borderColor: theme.colors.border,
73
+ borderRadius: spacing.radiusSm,
74
+ padding: spacing.xs,
75
+ minWidth: 128,
76
+ overflow: "hidden",
77
+ ...shadowStyle,
78
+ ...(Platform.OS === "web" && {
79
+ zIndex: 50,
80
+ cursor: "default",
81
+ }),
82
+ ...(styleOverride && typeof styleOverride !== "function"
83
+ ? StyleSheet.flatten(styleOverride)
84
+ : {}),
85
+ } }) }) }) }) }) }));
86
+ }
87
+ function DropdownMenuItem({ inset = false, variant = "default", style: styleOverride, ...props }) {
88
+ const { theme } = useTheme();
89
+ return (_jsx(TextClassContext.Provider, { value: "", children: _jsx(DropdownMenuPrimitive.Item, { ...props, style: {
90
+ position: "relative",
91
+ flexDirection: "row",
92
+ alignItems: "center",
93
+ gap: spacing.sm,
94
+ borderRadius: spacing.radiusSm,
95
+ paddingHorizontal: spacing.sm,
96
+ paddingVertical: Platform.select({ web: spacing.xs, default: spacing.sm }),
97
+ backgroundColor: "transparent",
98
+ ...(Platform.OS === "web" && {
99
+ cursor: props.disabled ? "not-allowed" : "pointer",
100
+ outlineStyle: "none",
101
+ }),
102
+ ...(props.disabled && { opacity: 0.5 }),
103
+ ...(inset && { paddingLeft: spacing.xl }),
104
+ ...(styleOverride && typeof styleOverride !== "function"
105
+ ? StyleSheet.flatten(styleOverride)
106
+ : {}),
107
+ } }) }));
108
+ }
109
+ function DropdownMenuCheckboxItem({ children, style: styleOverride, ...props }) {
110
+ const { theme } = useTheme();
111
+ return (_jsx(TextClassContext.Provider, { value: "", children: _jsxs(DropdownMenuPrimitive.CheckboxItem, { ...props, style: {
112
+ position: "relative",
113
+ flexDirection: "row",
114
+ alignItems: "center",
115
+ gap: spacing.sm,
116
+ borderRadius: spacing.radiusSm,
117
+ paddingVertical: Platform.select({ web: spacing.xs, default: spacing.sm }),
118
+ paddingLeft: spacing.xl,
119
+ paddingRight: spacing.sm,
120
+ backgroundColor: "transparent",
121
+ ...(Platform.OS === "web" && {
122
+ cursor: props.disabled ? "not-allowed" : "pointer",
123
+ outlineStyle: "none",
124
+ }),
125
+ ...(props.disabled && { opacity: 0.5 }),
126
+ ...(styleOverride && typeof styleOverride !== "function"
127
+ ? StyleSheet.flatten(styleOverride)
128
+ : {}),
129
+ }, children: [_jsx(View, { style: {
130
+ position: "absolute",
131
+ left: spacing.sm,
132
+ height: 14,
133
+ width: 14,
134
+ alignItems: "center",
135
+ justifyContent: "center",
136
+ }, children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx(Icon, { name: "check", size: 16, color: theme.colors.text, ...(Platform.OS === "web" && { style: { pointerEvents: "none" } }) }) }) }), typeof children === "function" ? null : children] }) }));
137
+ }
138
+ function DropdownMenuRadioItem({ children, style: styleOverride, ...props }) {
139
+ const { theme } = useTheme();
140
+ return (_jsx(TextClassContext.Provider, { value: "", children: _jsxs(DropdownMenuPrimitive.RadioItem, { ...props, style: {
141
+ position: "relative",
142
+ flexDirection: "row",
143
+ alignItems: "center",
144
+ gap: spacing.sm,
145
+ borderRadius: spacing.radiusSm,
146
+ paddingVertical: Platform.select({ web: spacing.xs, default: spacing.sm }),
147
+ paddingLeft: spacing.xl,
148
+ paddingRight: spacing.sm,
149
+ backgroundColor: "transparent",
150
+ ...(Platform.OS === "web" && {
151
+ cursor: props.disabled ? "not-allowed" : "pointer",
152
+ outlineStyle: "none",
153
+ }),
154
+ ...(props.disabled && { opacity: 0.5 }),
155
+ ...(styleOverride && typeof styleOverride !== "function"
156
+ ? StyleSheet.flatten(styleOverride)
157
+ : {}),
158
+ }, children: [_jsx(View, { style: {
159
+ position: "absolute",
160
+ left: spacing.sm,
161
+ height: 14,
162
+ width: 14,
163
+ alignItems: "center",
164
+ justifyContent: "center",
165
+ }, children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx(View, { style: {
166
+ backgroundColor: theme.colors.text,
167
+ height: 8,
168
+ width: 8,
169
+ borderRadius: 4,
170
+ } }) }) }), typeof children === "function" ? null : children] }) }));
171
+ }
172
+ function DropdownMenuLabel({ inset = false, style: styleOverride, ...props }) {
173
+ const { theme } = useTheme();
174
+ return (_jsx(DropdownMenuPrimitive.Label, { ...props, style: {
175
+ paddingHorizontal: spacing.sm,
176
+ paddingVertical: Platform.select({ web: spacing.xs, default: spacing.sm }),
177
+ fontSize: 14,
178
+ fontWeight: "500",
179
+ color: theme.colors.text,
180
+ ...(inset && { paddingLeft: spacing.xl }),
181
+ ...(styleOverride && typeof styleOverride !== "function"
182
+ ? StyleSheet.flatten(styleOverride)
183
+ : {}),
184
+ } }));
185
+ }
186
+ function DropdownMenuSeparator({ style: styleOverride, ...props }) {
187
+ const { theme } = useTheme();
188
+ return (_jsx(DropdownMenuPrimitive.Separator, { ...props, style: {
189
+ backgroundColor: theme.colors.border,
190
+ marginHorizontal: -spacing.xs,
191
+ marginVertical: spacing.xs,
192
+ height: 1,
193
+ ...(styleOverride && typeof styleOverride !== "function"
194
+ ? StyleSheet.flatten(styleOverride)
195
+ : {}),
196
+ } }));
197
+ }
198
+ function DropdownMenuShortcut({ style: styleOverride, ...props }) {
199
+ const { theme } = useTheme();
200
+ return (_jsx(Text, { ...props, style: [
201
+ {
202
+ marginLeft: "auto",
203
+ fontSize: 12,
204
+ letterSpacing: 2,
205
+ color: theme.colors.text,
206
+ opacity: 0.6,
207
+ },
208
+ styleOverride,
209
+ ] }));
210
+ }
211
+ export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, };
@@ -0,0 +1,42 @@
1
+ import React from "react";
2
+ import { StyleProp, ViewStyle } from "react-native";
3
+ import { type ButtonProps } from "./Button";
4
+ import { type IconName } from "./Icon";
5
+ export interface EmptyStateProps {
6
+ /** Icon name to display */
7
+ icon?: IconName;
8
+ /** Icon size in pixels */
9
+ iconSize?: number;
10
+ /** Title text */
11
+ title: string;
12
+ /** Description text */
13
+ description?: string;
14
+ /** CTA button label */
15
+ actionLabel?: string;
16
+ /** CTA button press handler */
17
+ onAction?: () => void;
18
+ /** Button preset variant */
19
+ actionPreset?: ButtonProps["preset"];
20
+ /** Custom style override */
21
+ style?: StyleProp<ViewStyle>;
22
+ /** Custom children below description / above action */
23
+ children?: React.ReactNode;
24
+ }
25
+ /**
26
+ * EmptyState Component
27
+ *
28
+ * Displays a centered placeholder for empty lists, search results,
29
+ * or blank screens. Works well as FlatList's ListEmptyComponent.
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * <EmptyState
34
+ * icon="inbox"
35
+ * title="No messages"
36
+ * description="You don't have any messages yet."
37
+ * actionLabel="Compose"
38
+ * onAction={() => router.push("/compose")}
39
+ * />
40
+ * ```
41
+ */
42
+ export declare function EmptyState({ icon, iconSize, title, description, actionLabel, onAction, actionPreset, style, children, }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,58 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { View, StyleSheet } from "react-native";
3
+ import { SansSerifBoldText, SansSerifText } from "./StyledText";
4
+ import { Button } from "./Button";
5
+ import { Icon } from "./Icon";
6
+ import { useTheme } from "../hooks/useTheme";
7
+ import { spacing } from "../constants/spacing";
8
+ /**
9
+ * EmptyState Component
10
+ *
11
+ * Displays a centered placeholder for empty lists, search results,
12
+ * or blank screens. Works well as FlatList's ListEmptyComponent.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * <EmptyState
17
+ * icon="inbox"
18
+ * title="No messages"
19
+ * description="You don't have any messages yet."
20
+ * actionLabel="Compose"
21
+ * onAction={() => router.push("/compose")}
22
+ * />
23
+ * ```
24
+ */
25
+ export function EmptyState({ icon, iconSize = 48, title, description, actionLabel, onAction, actionPreset = "default", style, children, }) {
26
+ const { theme } = useTheme();
27
+ const styles = createStyles(theme);
28
+ return (_jsxs(View, { style: [styles.container, style], children: [!!icon && (_jsx(View, { style: styles.iconWrapper, children: _jsx(Icon, { name: icon, size: iconSize, color: theme.colors.mutedForeground }) })), _jsx(SansSerifBoldText, { style: styles.title, children: title }), !!description && (_jsx(SansSerifText, { style: styles.description, children: description })), children, !!actionLabel && onAction && (_jsx(Button, { preset: actionPreset, onPress: onAction, style: styles.action, children: actionLabel }))] }));
29
+ }
30
+ const createStyles = (theme) => StyleSheet.create({
31
+ container: {
32
+ alignItems: "center",
33
+ justifyContent: "center",
34
+ paddingVertical: spacing.xxl,
35
+ paddingHorizontal: spacing.lg,
36
+ },
37
+ iconWrapper: {
38
+ marginBottom: spacing.md,
39
+ },
40
+ title: {
41
+ fontSize: 18,
42
+ lineHeight: 24,
43
+ color: theme.colors.foreground,
44
+ textAlign: "center",
45
+ letterSpacing: -0.3,
46
+ },
47
+ description: {
48
+ fontSize: 14,
49
+ lineHeight: 20,
50
+ color: theme.colors.mutedForeground,
51
+ textAlign: "center",
52
+ marginTop: spacing.sm,
53
+ maxWidth: 280,
54
+ },
55
+ action: {
56
+ marginTop: spacing.lg,
57
+ },
58
+ });
@@ -0,0 +1,53 @@
1
+ import React, { Component, ErrorInfo, ReactNode } from "react";
2
+ interface ErrorBoundaryProps {
3
+ /**
4
+ * When to catch errors
5
+ * - "always": Catch in all environments
6
+ * - "dev": Only catch in development
7
+ * - "prod": Only catch in production
8
+ * - "never": Never catch (errors bubble up)
9
+ */
10
+ catchErrors: "always" | "dev" | "prod" | "never";
11
+ /**
12
+ * Child components to render
13
+ */
14
+ children: ReactNode;
15
+ /**
16
+ * Custom error fallback component
17
+ */
18
+ FallbackComponent?: React.ComponentType<{
19
+ error: Error;
20
+ errorInfo: ErrorInfo | null;
21
+ resetError: () => void;
22
+ }>;
23
+ }
24
+ interface ErrorBoundaryState {
25
+ error: Error | null;
26
+ errorInfo: ErrorInfo | null;
27
+ }
28
+ /**
29
+ * ErrorBoundary catches JavaScript errors anywhere in the child component tree,
30
+ * logs those errors, and displays a fallback UI.
31
+ *
32
+ * Usage:
33
+ * ```tsx
34
+ * <ErrorBoundary catchErrors="always" FallbackComponent={ErrorScreen}>
35
+ * <App />
36
+ * </ErrorBoundary>
37
+ * ```
38
+ */
39
+ export declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
40
+ state: ErrorBoundaryState;
41
+ /**
42
+ * Determine if errors should be caught based on config and environment
43
+ */
44
+ private shouldCatchErrors;
45
+ /**
46
+ * Reset the error state to try rendering again
47
+ */
48
+ resetError: () => void;
49
+ static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState>;
50
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
51
+ render(): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
52
+ }
53
+ export {};
@@ -0,0 +1,75 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Component } from "react";
3
+ import { captureException } from "../lib/sentry";
4
+ /**
5
+ * ErrorBoundary catches JavaScript errors anywhere in the child component tree,
6
+ * logs those errors, and displays a fallback UI.
7
+ *
8
+ * Usage:
9
+ * ```tsx
10
+ * <ErrorBoundary catchErrors="always" FallbackComponent={ErrorScreen}>
11
+ * <App />
12
+ * </ErrorBoundary>
13
+ * ```
14
+ */
15
+ export class ErrorBoundary extends Component {
16
+ state = {
17
+ error: null,
18
+ errorInfo: null,
19
+ };
20
+ /**
21
+ * Determine if errors should be caught based on config and environment
22
+ */
23
+ shouldCatchErrors() {
24
+ const { catchErrors } = this.props;
25
+ if (catchErrors === "always")
26
+ return true;
27
+ if (catchErrors === "never")
28
+ return false;
29
+ if (catchErrors === "dev")
30
+ return __DEV__;
31
+ if (catchErrors === "prod")
32
+ return !__DEV__;
33
+ return true;
34
+ }
35
+ /**
36
+ * Reset the error state to try rendering again
37
+ */
38
+ resetError = () => {
39
+ this.setState({ error: null, errorInfo: null });
40
+ };
41
+ static getDerivedStateFromError(error) {
42
+ return { error };
43
+ }
44
+ componentDidCatch(error, errorInfo) {
45
+ // Update state with error info
46
+ this.setState({ errorInfo });
47
+ // Log error to console in development
48
+ if (__DEV__) {
49
+ console.error("ErrorBoundary caught an error:", error);
50
+ console.error("Error info:", errorInfo);
51
+ }
52
+ // Report to Sentry (no-op if DSN not configured)
53
+ captureException(error, {
54
+ contexts: {
55
+ react: {
56
+ componentStack: errorInfo?.componentStack ?? undefined,
57
+ },
58
+ },
59
+ });
60
+ }
61
+ render() {
62
+ const { children, FallbackComponent } = this.props;
63
+ const { error, errorInfo } = this.state;
64
+ // If no error or we shouldn't catch, render children normally
65
+ if (!error || !this.shouldCatchErrors()) {
66
+ return children;
67
+ }
68
+ // If custom fallback component provided, use it
69
+ if (FallbackComponent) {
70
+ return (_jsx(FallbackComponent, { error: error, errorInfo: errorInfo, resetError: this.resetError }));
71
+ }
72
+ // Default: just return null (you should provide a FallbackComponent)
73
+ return null;
74
+ }
75
+ }
@@ -0,0 +1,46 @@
1
+ import * as React from "react";
2
+ import { StyleProp, ViewStyle } from "react-native";
3
+ import Feather from "@expo/vector-icons/Feather";
4
+ /**
5
+ * Theme color names that can be used as shortcuts
6
+ * Only includes colors that actually exist in the theme
7
+ */
8
+ export type ThemeColorName = "primary" | "primaryForeground" | "secondary" | "muted" | "destructive" | "success" | "warning" | "text" | "textDim";
9
+ export type IconName = React.ComponentProps<typeof Feather>["name"];
10
+ type IconBaseProps = {
11
+ /** Size of the icon in pixels */
12
+ size?: number;
13
+ /** Icon color - can be a hex color or a theme color name. Defaults to theme's text color */
14
+ color?: string | ThemeColorName;
15
+ /** Additional styles for positioning, transforms, etc. */
16
+ style?: StyleProp<ViewStyle>;
17
+ /** When true, hides the icon from the accessibility tree. @default false */
18
+ decorative?: boolean;
19
+ };
20
+ type FeatherIconProps = IconBaseProps & {
21
+ /** The icon name to render (Feather icons) */
22
+ name: IconName;
23
+ component?: never;
24
+ };
25
+ type CustomIconProps = IconBaseProps & {
26
+ name?: never;
27
+ /** Custom component to render instead of Feather. Receives size and color as props. */
28
+ component: React.ComponentType<{
29
+ size: number;
30
+ color: string;
31
+ }>;
32
+ };
33
+ export type IconProps = FeatherIconProps | CustomIconProps;
34
+ /**
35
+ * Universal Icon Component
36
+ * Wraps @expo/vector-icons Feather with theme integration and style support
37
+ *
38
+ * Usage:
39
+ * ```tsx
40
+ * <Icon name="check" color="primary" size={16} />
41
+ * <Icon name="calendar" color="#FF0000" size={24} />
42
+ * <Icon name="terminal" style={{ marginRight: 8 }} />
43
+ * ```
44
+ */
45
+ export declare function Icon(props: IconProps): import("react/jsx-runtime").JSX.Element;
46
+ export {};
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { View } from "react-native";
3
+ import { useTheme } from "../hooks/useTheme";
4
+ import Feather from "@expo/vector-icons/Feather";
5
+ const THEME_COLOR_KEYS = [
6
+ "primary", "primaryForeground", "secondary", "muted",
7
+ "destructive", "success", "warning", "text", "textDim",
8
+ ];
9
+ function resolveIconColor(color, themeColors) {
10
+ if (!color)
11
+ return themeColors.text;
12
+ if (THEME_COLOR_KEYS.includes(color)) {
13
+ return themeColors[color];
14
+ }
15
+ return color;
16
+ }
17
+ /**
18
+ * Universal Icon Component
19
+ * Wraps @expo/vector-icons Feather with theme integration and style support
20
+ *
21
+ * Usage:
22
+ * ```tsx
23
+ * <Icon name="check" color="primary" size={16} />
24
+ * <Icon name="calendar" color="#FF0000" size={24} />
25
+ * <Icon name="terminal" style={{ marginRight: 8 }} />
26
+ * ```
27
+ */
28
+ export function Icon(props) {
29
+ const { size = 24, color, style, decorative = false } = props;
30
+ const { theme } = useTheme();
31
+ const iconColor = resolveIconColor(color, theme.colors);
32
+ const CustomComponent = "component" in props ? props.component : undefined;
33
+ // Wrap in View with pointerEvents="none" to prevent icons from
34
+ // intercepting touches when used inside TouchableOpacity on iOS
35
+ return (_jsx(View, { pointerEvents: "none", style: style, accessible: !decorative, ...(decorative && {
36
+ importantForAccessibility: "no",
37
+ accessibilityElementsHidden: true,
38
+ "aria-hidden": true,
39
+ }), children: CustomComponent ? (_jsx(CustomComponent, { size: size, color: iconColor })) : (_jsx(Feather, { name: props.name, size: size, color: iconColor })) }));
40
+ }
@@ -0,0 +1,72 @@
1
+ import { StyleProp, ViewStyle } from "react-native";
2
+ export interface InputOTPProps {
3
+ /**
4
+ * Number of OTP cells
5
+ * @default 6
6
+ */
7
+ length?: number;
8
+ /**
9
+ * Current value
10
+ * @default ""
11
+ */
12
+ value?: string;
13
+ /**
14
+ * Called when the value changes
15
+ */
16
+ onChangeText?: (value: string) => void;
17
+ /**
18
+ * Called when all cells are filled
19
+ */
20
+ onComplete?: (value: string) => void;
21
+ /**
22
+ * Whether the input is in an error state
23
+ * @default false
24
+ */
25
+ error?: boolean;
26
+ /**
27
+ * Error message displayed below the cells
28
+ */
29
+ errorText?: string;
30
+ /**
31
+ * Whether the input is disabled
32
+ * @default false
33
+ */
34
+ disabled?: boolean;
35
+ /**
36
+ * Whether to auto-focus the input on mount
37
+ * @default false
38
+ */
39
+ autoFocus?: boolean;
40
+ /**
41
+ * Whether to mask the input with bullet characters
42
+ * @default false
43
+ */
44
+ secureTextEntry?: boolean;
45
+ /**
46
+ * Input mode for the keyboard
47
+ * @default "numeric"
48
+ */
49
+ inputMode?: "numeric" | "text";
50
+ /**
51
+ * Custom style override for the container
52
+ */
53
+ style?: StyleProp<ViewStyle>;
54
+ }
55
+ /**
56
+ * OTP/verification code input with individual character cells.
57
+ *
58
+ * A single hidden TextInput captures keyboard input. Visible cells are
59
+ * Pressable views that focus the hidden input on tap.
60
+ *
61
+ * Usage:
62
+ * ```tsx
63
+ * <InputOTP
64
+ * length={6}
65
+ * value={code}
66
+ * onChangeText={setCode}
67
+ * onComplete={(code) => verify(code)}
68
+ * />
69
+ * ```
70
+ */
71
+ declare function InputOTP({ length, value, onChangeText, onComplete, error, errorText, disabled, autoFocus, secureTextEntry, inputMode, style: styleOverride, }: InputOTPProps): import("react/jsx-runtime").JSX.Element;
72
+ export { InputOTP };