@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.
- package/README.md +96 -0
- package/dist/components/Accordion.d.ts +54 -0
- package/dist/components/Accordion.js +149 -0
- package/dist/components/Alert.d.ts +30 -0
- package/dist/components/Alert.js +25 -0
- package/dist/components/AnimatedView.d.ts +55 -0
- package/dist/components/AnimatedView.js +39 -0
- package/dist/components/Badge.d.ts +23 -0
- package/dist/components/Badge.js +74 -0
- package/dist/components/BottomSheet.d.ts +74 -0
- package/dist/components/BottomSheet.js +513 -0
- package/dist/components/Button.d.ts +129 -0
- package/dist/components/Button.js +216 -0
- package/dist/components/Card.d.ts +42 -0
- package/dist/components/Card.js +126 -0
- package/dist/components/Checkbox.d.ts +39 -0
- package/dist/components/Checkbox.js +96 -0
- package/dist/components/Collapsible.d.ts +67 -0
- package/dist/components/Collapsible.js +38 -0
- package/dist/components/Dialog.d.ts +140 -0
- package/dist/components/Dialog.js +167 -0
- package/dist/components/DismissKeyboard.d.ts +15 -0
- package/dist/components/DismissKeyboard.js +13 -0
- package/dist/components/Drawer.d.ts +74 -0
- package/dist/components/Drawer.js +423 -0
- package/dist/components/DropdownMenu.d.ts +120 -0
- package/dist/components/DropdownMenu.js +211 -0
- package/dist/components/EmptyState.d.ts +42 -0
- package/dist/components/EmptyState.js +58 -0
- package/dist/components/ErrorBoundary.d.ts +53 -0
- package/dist/components/ErrorBoundary.js +75 -0
- package/dist/components/Icon.d.ts +46 -0
- package/dist/components/Icon.js +40 -0
- package/dist/components/InputOTP.d.ts +72 -0
- package/dist/components/InputOTP.js +155 -0
- package/dist/components/Label.d.ts +61 -0
- package/dist/components/Label.js +72 -0
- package/dist/components/MaxWidthContainer.d.ts +58 -0
- package/dist/components/MaxWidthContainer.js +64 -0
- package/dist/components/Notification.d.ts +26 -0
- package/dist/components/Notification.js +230 -0
- package/dist/components/Popover.d.ts +79 -0
- package/dist/components/Popover.js +91 -0
- package/dist/components/Progress.d.ts +28 -0
- package/dist/components/Progress.js +107 -0
- package/dist/components/RadioGroup.d.ts +65 -0
- package/dist/components/RadioGroup.js +142 -0
- package/dist/components/Select.d.ts +88 -0
- package/dist/components/Select.js +172 -0
- package/dist/components/Separator.d.ts +83 -0
- package/dist/components/Separator.js +85 -0
- package/dist/components/Skeleton.d.ts +68 -0
- package/dist/components/Skeleton.js +99 -0
- package/dist/components/Slider.d.ts +24 -0
- package/dist/components/Slider.js +162 -0
- package/dist/components/StatusBar.d.ts +1 -0
- package/dist/components/StatusBar.js +19 -0
- package/dist/components/StyledText.d.ts +161 -0
- package/dist/components/StyledText.js +193 -0
- package/dist/components/Switch.d.ts +44 -0
- package/dist/components/Switch.js +129 -0
- package/dist/components/Tabs.d.ts +31 -0
- package/dist/components/Tabs.js +127 -0
- package/dist/components/TextInput.d.ts +120 -0
- package/dist/components/TextInput.js +263 -0
- package/dist/components/Toggle.d.ts +106 -0
- package/dist/components/Toggle.js +150 -0
- package/dist/components/ToggleGroup.d.ts +80 -0
- package/dist/components/ToggleGroup.js +189 -0
- package/dist/components/Tooltip.d.ts +121 -0
- package/dist/components/Tooltip.js +132 -0
- package/dist/components/index.d.ts +35 -0
- package/dist/components/index.js +35 -0
- package/dist/constants/colors.d.ts +82 -0
- package/dist/constants/colors.js +116 -0
- package/dist/constants/fonts.d.ts +32 -0
- package/dist/constants/fonts.js +91 -0
- package/dist/constants/index.d.ts +3 -0
- package/dist/constants/index.js +3 -0
- package/dist/constants/spacing.d.ts +40 -0
- package/dist/constants/spacing.js +48 -0
- package/dist/hooks/index.d.ts +6 -0
- package/dist/hooks/index.js +6 -0
- package/dist/hooks/useDimensions.d.ts +19 -0
- package/dist/hooks/useDimensions.js +55 -0
- package/dist/hooks/useReduceMotion.d.ts +5 -0
- package/dist/hooks/useReduceMotion.js +64 -0
- package/dist/hooks/useResources.d.ts +12 -0
- package/dist/hooks/useResources.js +56 -0
- package/dist/hooks/useScalePress.d.ts +57 -0
- package/dist/hooks/useScalePress.js +55 -0
- package/dist/hooks/useStaggeredEntrance.d.ts +67 -0
- package/dist/hooks/useStaggeredEntrance.js +74 -0
- package/dist/hooks/useTheme.d.ts +88 -0
- package/dist/hooks/useTheme.js +328 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/lib/animations.d.ts +1 -0
- package/dist/lib/animations.js +3 -0
- package/dist/lib/haptics.d.ts +3 -0
- package/dist/lib/haptics.js +29 -0
- package/dist/lib/index.d.ts +3 -0
- package/dist/lib/index.js +3 -0
- package/dist/lib/sentry.d.ts +16 -0
- package/dist/lib/sentry.js +55 -0
- package/dist/state/globalUIStore.d.ts +30 -0
- package/dist/state/globalUIStore.js +8 -0
- package/dist/state/index.d.ts +2 -0
- package/dist/state/index.js +2 -0
- package/dist/state/themeStore.d.ts +6 -0
- package/dist/state/themeStore.js +38 -0
- package/package.json +92 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Platform, StyleSheet, View } from "react-native";
|
|
4
|
+
import { Icon } from "./Icon";
|
|
5
|
+
import { AnimatedView } from "./AnimatedView";
|
|
6
|
+
import { TextClassContext, TextColorContext } from "./StyledText";
|
|
7
|
+
import { useTheme } from "../hooks/useTheme";
|
|
8
|
+
import { spacing } from "../constants/spacing";
|
|
9
|
+
import { palette } from "../constants/colors";
|
|
10
|
+
import * as SelectPrimitive from "@rn-primitives/select";
|
|
11
|
+
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens";
|
|
12
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
13
|
+
// Platform-specific overlay
|
|
14
|
+
const FullWindowOverlay = Platform.OS === "ios" ? RNFullWindowOverlay : React.Fragment;
|
|
15
|
+
/**
|
|
16
|
+
* SelectRoot Component
|
|
17
|
+
* Manages select state and provides context for trigger and content
|
|
18
|
+
*/
|
|
19
|
+
const SelectRoot = SelectPrimitive.Root;
|
|
20
|
+
const SIZE_CONFIGS = {
|
|
21
|
+
sm: {
|
|
22
|
+
height: 32,
|
|
23
|
+
fontSize: 12,
|
|
24
|
+
paddingHorizontal: spacing.sm,
|
|
25
|
+
},
|
|
26
|
+
md: {
|
|
27
|
+
height: 36,
|
|
28
|
+
fontSize: 14,
|
|
29
|
+
paddingHorizontal: spacing.inputPadding,
|
|
30
|
+
},
|
|
31
|
+
lg: {
|
|
32
|
+
height: 40,
|
|
33
|
+
fontSize: 14,
|
|
34
|
+
paddingHorizontal: spacing.md,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
function SelectTrigger({ size = "md", error = false, children, style: styleOverride, disabled, ...props }) {
|
|
38
|
+
const { theme } = useTheme();
|
|
39
|
+
const sizeConfig = SIZE_CONFIGS[size];
|
|
40
|
+
return (_jsxs(SelectPrimitive.Trigger, { disabled: disabled, ...props, style: {
|
|
41
|
+
flexDirection: "row",
|
|
42
|
+
justifyContent: "space-between",
|
|
43
|
+
alignItems: "center",
|
|
44
|
+
height: sizeConfig.height,
|
|
45
|
+
paddingHorizontal: sizeConfig.paddingHorizontal,
|
|
46
|
+
borderWidth: 1,
|
|
47
|
+
borderColor: error ? theme.colors.destructive : theme.colors.border,
|
|
48
|
+
borderRadius: spacing.radiusMd,
|
|
49
|
+
backgroundColor: theme.colors.background,
|
|
50
|
+
...(Platform.OS === "web" && {
|
|
51
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
52
|
+
outlineStyle: "none",
|
|
53
|
+
}),
|
|
54
|
+
...(disabled && { opacity: 0.5 }),
|
|
55
|
+
...(styleOverride && typeof styleOverride !== "function"
|
|
56
|
+
? StyleSheet.flatten(styleOverride)
|
|
57
|
+
: {}),
|
|
58
|
+
}, children: [typeof children === "function" ? null : children, _jsx(Icon, { name: "chevron-down", size: 16, color: theme.colors.mutedForeground })] }));
|
|
59
|
+
}
|
|
60
|
+
function SelectValue({ size = "md", placeholder, style: styleOverride, ...props }) {
|
|
61
|
+
const { theme } = useTheme();
|
|
62
|
+
const sizeConfig = SIZE_CONFIGS[size];
|
|
63
|
+
return (_jsx(SelectPrimitive.Value, { placeholder: placeholder, ...props, style: {
|
|
64
|
+
fontSize: sizeConfig.fontSize,
|
|
65
|
+
color: theme.colors.text,
|
|
66
|
+
flex: 1,
|
|
67
|
+
...(styleOverride && typeof styleOverride !== "function"
|
|
68
|
+
? StyleSheet.flatten(styleOverride)
|
|
69
|
+
: {}),
|
|
70
|
+
} }));
|
|
71
|
+
}
|
|
72
|
+
function SelectContent({ side, align = "start", sideOffset = 4, portalHost, style: styleOverride, ...props }) {
|
|
73
|
+
const { theme, getShadowStyle, getContrastingColor } = useTheme();
|
|
74
|
+
const shadowStyle = StyleSheet.flatten(getShadowStyle("soft"));
|
|
75
|
+
const insets = useSafeAreaInsets();
|
|
76
|
+
const textColor = getContrastingColor(theme.colors.background, palette.white, palette.black);
|
|
77
|
+
return (_jsx(SelectPrimitive.Portal, { hostName: portalHost, children: _jsx(FullWindowOverlay, { children: _jsx(SelectPrimitive.Overlay, { style: Platform.select({
|
|
78
|
+
native: StyleSheet.absoluteFill,
|
|
79
|
+
default: undefined,
|
|
80
|
+
}), children: _jsx(AnimatedView, { type: "fade", children: _jsx(TextColorContext.Provider, { value: textColor, children: _jsx(TextClassContext.Provider, { value: "", children: _jsx(SelectPrimitive.Content, { side: side, align: align, sideOffset: sideOffset, insets: insets, avoidCollisions: true, ...props, style: {
|
|
81
|
+
backgroundColor: theme.colors.background,
|
|
82
|
+
borderWidth: 1,
|
|
83
|
+
borderColor: theme.colors.border,
|
|
84
|
+
borderRadius: spacing.radiusSm,
|
|
85
|
+
padding: spacing.xs,
|
|
86
|
+
minWidth: 128,
|
|
87
|
+
overflow: "hidden",
|
|
88
|
+
...shadowStyle,
|
|
89
|
+
...(Platform.OS === "web" && {
|
|
90
|
+
zIndex: 50,
|
|
91
|
+
cursor: "default",
|
|
92
|
+
}),
|
|
93
|
+
...(styleOverride && typeof styleOverride !== "function"
|
|
94
|
+
? StyleSheet.flatten(styleOverride)
|
|
95
|
+
: {}),
|
|
96
|
+
} }) }) }) }) }) }) }));
|
|
97
|
+
}
|
|
98
|
+
function SelectItem({ children, style: styleOverride, ...props }) {
|
|
99
|
+
const { theme } = useTheme();
|
|
100
|
+
return (_jsx(TextClassContext.Provider, { value: "", children: _jsxs(SelectPrimitive.Item, { ...props, style: {
|
|
101
|
+
position: "relative",
|
|
102
|
+
flexDirection: "row",
|
|
103
|
+
alignItems: "center",
|
|
104
|
+
gap: spacing.sm,
|
|
105
|
+
borderRadius: spacing.radiusSm,
|
|
106
|
+
paddingVertical: Platform.select({ web: spacing.xs, default: spacing.sm }),
|
|
107
|
+
paddingLeft: spacing.xl,
|
|
108
|
+
paddingRight: spacing.sm,
|
|
109
|
+
backgroundColor: "transparent",
|
|
110
|
+
...(Platform.OS === "web" && {
|
|
111
|
+
cursor: props.disabled ? "not-allowed" : "pointer",
|
|
112
|
+
outlineStyle: "none",
|
|
113
|
+
}),
|
|
114
|
+
...(props.disabled && { opacity: 0.5 }),
|
|
115
|
+
...(styleOverride && typeof styleOverride !== "function"
|
|
116
|
+
? StyleSheet.flatten(styleOverride)
|
|
117
|
+
: {}),
|
|
118
|
+
}, children: [_jsx(View, { style: {
|
|
119
|
+
position: "absolute",
|
|
120
|
+
left: spacing.sm,
|
|
121
|
+
height: 14,
|
|
122
|
+
width: 14,
|
|
123
|
+
alignItems: "center",
|
|
124
|
+
justifyContent: "center",
|
|
125
|
+
}, children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(Icon, { name: "check", size: 16, color: theme.colors.text, ...(Platform.OS === "web" && { style: { pointerEvents: "none" } }) }) }) }), _jsx(SelectPrimitive.ItemText, {}), typeof children === "function" ? null : children] }) }));
|
|
126
|
+
}
|
|
127
|
+
function SelectGroup({ style: styleOverride, ...props }) {
|
|
128
|
+
return (_jsx(SelectPrimitive.Group, { ...props, style: {
|
|
129
|
+
...(styleOverride && typeof styleOverride !== "function"
|
|
130
|
+
? StyleSheet.flatten(styleOverride)
|
|
131
|
+
: {}),
|
|
132
|
+
} }));
|
|
133
|
+
}
|
|
134
|
+
function SelectLabel({ style: styleOverride, ...props }) {
|
|
135
|
+
const { theme } = useTheme();
|
|
136
|
+
return (_jsx(SelectPrimitive.Label, { ...props, style: {
|
|
137
|
+
paddingHorizontal: spacing.sm,
|
|
138
|
+
paddingVertical: Platform.select({ web: spacing.xs, default: spacing.sm }),
|
|
139
|
+
fontSize: 14,
|
|
140
|
+
fontWeight: "500",
|
|
141
|
+
color: theme.colors.text,
|
|
142
|
+
...(styleOverride && typeof styleOverride !== "function"
|
|
143
|
+
? StyleSheet.flatten(styleOverride)
|
|
144
|
+
: {}),
|
|
145
|
+
} }));
|
|
146
|
+
}
|
|
147
|
+
function SelectSeparator({ style: styleOverride, ...props }) {
|
|
148
|
+
const { theme } = useTheme();
|
|
149
|
+
return (_jsx(SelectPrimitive.Separator, { ...props, style: {
|
|
150
|
+
backgroundColor: theme.colors.border,
|
|
151
|
+
marginHorizontal: -spacing.xs,
|
|
152
|
+
marginVertical: spacing.xs,
|
|
153
|
+
height: 1,
|
|
154
|
+
...(styleOverride && typeof styleOverride !== "function"
|
|
155
|
+
? StyleSheet.flatten(styleOverride)
|
|
156
|
+
: {}),
|
|
157
|
+
} }));
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Select Component with Sub-components
|
|
161
|
+
* Properly typed interface for dot notation access (e.g., Select.Trigger)
|
|
162
|
+
*/
|
|
163
|
+
const Select = Object.assign(SelectRoot, {
|
|
164
|
+
Trigger: SelectTrigger,
|
|
165
|
+
Value: SelectValue,
|
|
166
|
+
Content: SelectContent,
|
|
167
|
+
Item: SelectItem,
|
|
168
|
+
Group: SelectGroup,
|
|
169
|
+
Label: SelectLabel,
|
|
170
|
+
Separator: SelectSeparator,
|
|
171
|
+
});
|
|
172
|
+
export { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SelectGroup, SelectLabel, SelectSeparator, };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { StyleProp, ViewStyle } from "react-native";
|
|
2
|
+
import * as SeparatorPrimitive from "@rn-primitives/separator";
|
|
3
|
+
/**
|
|
4
|
+
* Size variants for Separator thickness
|
|
5
|
+
*/
|
|
6
|
+
export type SeparatorSize = "sm" | "md" | "lg";
|
|
7
|
+
/**
|
|
8
|
+
* Visual variants for Separator
|
|
9
|
+
*/
|
|
10
|
+
export type SeparatorVariant = "default" | "muted" | "primary";
|
|
11
|
+
export interface SeparatorProps extends Omit<SeparatorPrimitive.RootProps, "style"> {
|
|
12
|
+
/**
|
|
13
|
+
* Orientation of the separator
|
|
14
|
+
* @default "horizontal"
|
|
15
|
+
*/
|
|
16
|
+
orientation?: "horizontal" | "vertical";
|
|
17
|
+
/**
|
|
18
|
+
* Whether the separator is purely decorative (no semantic meaning)
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
decorative?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Size variant controlling thickness
|
|
24
|
+
* @default "sm"
|
|
25
|
+
*/
|
|
26
|
+
size?: SeparatorSize;
|
|
27
|
+
/**
|
|
28
|
+
* Visual variant controlling color
|
|
29
|
+
* @default "default"
|
|
30
|
+
*/
|
|
31
|
+
variant?: SeparatorVariant;
|
|
32
|
+
/**
|
|
33
|
+
* Custom style override (uses StyleSheet.flatten for web compatibility)
|
|
34
|
+
*/
|
|
35
|
+
style?: StyleProp<ViewStyle>;
|
|
36
|
+
/**
|
|
37
|
+
* Margin around the separator
|
|
38
|
+
* For horizontal: vertical margin, for vertical: horizontal margin
|
|
39
|
+
* @default spacing.md (16)
|
|
40
|
+
*/
|
|
41
|
+
margin?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Enhanced Separator Component
|
|
45
|
+
*
|
|
46
|
+
* A visual or semantic distinction between content sections.
|
|
47
|
+
* Supports both horizontal and vertical orientations with
|
|
48
|
+
* theme-aware styling.
|
|
49
|
+
*
|
|
50
|
+
* Features:
|
|
51
|
+
* - Orientation (horizontal, vertical)
|
|
52
|
+
* - Size variants (sm, md, lg)
|
|
53
|
+
* - Visual variants (default, muted, primary)
|
|
54
|
+
* - Configurable margin
|
|
55
|
+
* - Theme-aware colors
|
|
56
|
+
* - Full accessibility support
|
|
57
|
+
*
|
|
58
|
+
* Usage:
|
|
59
|
+
* ```tsx
|
|
60
|
+
* // Basic horizontal separator
|
|
61
|
+
* <Separator />
|
|
62
|
+
*
|
|
63
|
+
* // Vertical separator
|
|
64
|
+
* <Separator orientation="vertical" />
|
|
65
|
+
*
|
|
66
|
+
* // With size variant
|
|
67
|
+
* <Separator size="lg" />
|
|
68
|
+
*
|
|
69
|
+
* // Primary colored separator
|
|
70
|
+
* <Separator variant="primary" />
|
|
71
|
+
*
|
|
72
|
+
* // Muted separator with custom margin
|
|
73
|
+
* <Separator variant="muted" margin={spacing.lg} />
|
|
74
|
+
*
|
|
75
|
+
* // Non-decorative separator (has semantic meaning)
|
|
76
|
+
* <Separator decorative={false} />
|
|
77
|
+
*
|
|
78
|
+
* // Custom style
|
|
79
|
+
* <Separator style={{ opacity: 0.5 }} />
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
declare function Separator({ orientation, decorative, size, variant, style: styleOverride, margin, ...props }: SeparatorProps): import("react/jsx-runtime").JSX.Element;
|
|
83
|
+
export { Separator };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { StyleSheet } from "react-native";
|
|
3
|
+
import { useTheme } from "../hooks/useTheme";
|
|
4
|
+
import { spacing } from "../constants/spacing";
|
|
5
|
+
import * as SeparatorPrimitive from "@rn-primitives/separator";
|
|
6
|
+
const SIZE_CONFIGS = {
|
|
7
|
+
sm: 1,
|
|
8
|
+
md: 2,
|
|
9
|
+
lg: 4,
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Enhanced Separator Component
|
|
13
|
+
*
|
|
14
|
+
* A visual or semantic distinction between content sections.
|
|
15
|
+
* Supports both horizontal and vertical orientations with
|
|
16
|
+
* theme-aware styling.
|
|
17
|
+
*
|
|
18
|
+
* Features:
|
|
19
|
+
* - Orientation (horizontal, vertical)
|
|
20
|
+
* - Size variants (sm, md, lg)
|
|
21
|
+
* - Visual variants (default, muted, primary)
|
|
22
|
+
* - Configurable margin
|
|
23
|
+
* - Theme-aware colors
|
|
24
|
+
* - Full accessibility support
|
|
25
|
+
*
|
|
26
|
+
* Usage:
|
|
27
|
+
* ```tsx
|
|
28
|
+
* // Basic horizontal separator
|
|
29
|
+
* <Separator />
|
|
30
|
+
*
|
|
31
|
+
* // Vertical separator
|
|
32
|
+
* <Separator orientation="vertical" />
|
|
33
|
+
*
|
|
34
|
+
* // With size variant
|
|
35
|
+
* <Separator size="lg" />
|
|
36
|
+
*
|
|
37
|
+
* // Primary colored separator
|
|
38
|
+
* <Separator variant="primary" />
|
|
39
|
+
*
|
|
40
|
+
* // Muted separator with custom margin
|
|
41
|
+
* <Separator variant="muted" margin={spacing.lg} />
|
|
42
|
+
*
|
|
43
|
+
* // Non-decorative separator (has semantic meaning)
|
|
44
|
+
* <Separator decorative={false} />
|
|
45
|
+
*
|
|
46
|
+
* // Custom style
|
|
47
|
+
* <Separator style={{ opacity: 0.5 }} />
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
function Separator({ orientation = "horizontal", decorative = true, size = "sm", variant = "default", style: styleOverride, margin = spacing.md, ...props }) {
|
|
51
|
+
const { theme } = useTheme();
|
|
52
|
+
// Determine color based on variant
|
|
53
|
+
const getColor = () => {
|
|
54
|
+
switch (variant) {
|
|
55
|
+
case "primary":
|
|
56
|
+
return theme.colors.primary;
|
|
57
|
+
case "muted":
|
|
58
|
+
return theme.colors.muted;
|
|
59
|
+
case "default":
|
|
60
|
+
default:
|
|
61
|
+
return theme.colors.border;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const thickness = SIZE_CONFIGS[size];
|
|
65
|
+
const color = getColor();
|
|
66
|
+
// Flatten style override for web compatibility
|
|
67
|
+
const flattenedStyle = styleOverride ? StyleSheet.flatten(styleOverride) : undefined;
|
|
68
|
+
const isHorizontal = orientation === "horizontal";
|
|
69
|
+
return (_jsx(SeparatorPrimitive.Root, { ...props, orientation: orientation, decorative: decorative, style: {
|
|
70
|
+
backgroundColor: color,
|
|
71
|
+
...(isHorizontal
|
|
72
|
+
? {
|
|
73
|
+
height: thickness,
|
|
74
|
+
width: "100%",
|
|
75
|
+
marginVertical: margin,
|
|
76
|
+
}
|
|
77
|
+
: {
|
|
78
|
+
width: thickness,
|
|
79
|
+
height: "100%",
|
|
80
|
+
marginHorizontal: margin,
|
|
81
|
+
}),
|
|
82
|
+
...(flattenedStyle || {}),
|
|
83
|
+
} }));
|
|
84
|
+
}
|
|
85
|
+
export { Separator };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { StyleProp, ViewStyle } from "react-native";
|
|
2
|
+
export interface SkeletonProps {
|
|
3
|
+
/** Width of the skeleton element */
|
|
4
|
+
width?: number | `${number}%`;
|
|
5
|
+
/** Height of the skeleton element */
|
|
6
|
+
height?: number;
|
|
7
|
+
/** Border radius (defaults to radiusMd) */
|
|
8
|
+
borderRadius?: number;
|
|
9
|
+
/** Render as a circle (overrides borderRadius) */
|
|
10
|
+
circle?: boolean;
|
|
11
|
+
/** Custom style override */
|
|
12
|
+
style?: StyleProp<ViewStyle>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Skeleton Component
|
|
16
|
+
*
|
|
17
|
+
* A placeholder loading element with a pulsing shimmer animation.
|
|
18
|
+
* Respects reduced-motion accessibility settings.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* <Skeleton width={200} height={20} />
|
|
23
|
+
* <Skeleton width={40} height={40} circle />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function Skeleton({ width, height, borderRadius, circle, style, }: SkeletonProps): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
export interface SkeletonTextProps {
|
|
28
|
+
/** Number of text lines to render */
|
|
29
|
+
lines?: number;
|
|
30
|
+
/** Height of each line */
|
|
31
|
+
lineHeight?: number;
|
|
32
|
+
/** Gap between lines */
|
|
33
|
+
gap?: number;
|
|
34
|
+
/** Custom style override */
|
|
35
|
+
style?: StyleProp<ViewStyle>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* SkeletonText renders N horizontal bars simulating text.
|
|
39
|
+
* The last line is rendered at 60% width for a natural look.
|
|
40
|
+
*/
|
|
41
|
+
export declare function SkeletonText({ lines, lineHeight, gap, style, }: SkeletonTextProps): import("react/jsx-runtime").JSX.Element;
|
|
42
|
+
export interface SkeletonAvatarProps {
|
|
43
|
+
/** Diameter of the circular avatar placeholder */
|
|
44
|
+
size?: number;
|
|
45
|
+
/** Custom style override */
|
|
46
|
+
style?: StyleProp<ViewStyle>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* SkeletonAvatar is a circular skeleton shorthand.
|
|
50
|
+
*/
|
|
51
|
+
export declare function SkeletonAvatar({ size, style }: SkeletonAvatarProps): import("react/jsx-runtime").JSX.Element;
|
|
52
|
+
export interface SkeletonCardProps {
|
|
53
|
+
/** Whether to show an image placeholder area */
|
|
54
|
+
showImage?: boolean;
|
|
55
|
+
/** Image placeholder height */
|
|
56
|
+
imageHeight?: number;
|
|
57
|
+
/** Whether to show an avatar row */
|
|
58
|
+
showAvatar?: boolean;
|
|
59
|
+
/** Number of text lines */
|
|
60
|
+
textLines?: number;
|
|
61
|
+
/** Custom style override */
|
|
62
|
+
style?: StyleProp<ViewStyle>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* SkeletonCard composes an image placeholder, optional avatar row,
|
|
66
|
+
* and text lines in a Card-styled wrapper.
|
|
67
|
+
*/
|
|
68
|
+
export declare function SkeletonCard({ showImage, imageHeight, showAvatar, textLines, style, }: SkeletonCardProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { View, Animated, StyleSheet } from "react-native";
|
|
4
|
+
import { useReducedMotion } from "react-native-reanimated";
|
|
5
|
+
import { useTheme } from "../hooks/useTheme";
|
|
6
|
+
import { shouldUseNativeDriver } from "../lib/animations";
|
|
7
|
+
import { spacing } from "../constants/spacing";
|
|
8
|
+
/**
|
|
9
|
+
* Skeleton Component
|
|
10
|
+
*
|
|
11
|
+
* A placeholder loading element with a pulsing shimmer animation.
|
|
12
|
+
* Respects reduced-motion accessibility settings.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* <Skeleton width={200} height={20} />
|
|
17
|
+
* <Skeleton width={40} height={40} circle />
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function Skeleton({ width = "100%", height = 20, borderRadius = spacing.radiusMd, circle = false, style, }) {
|
|
21
|
+
const { theme } = useTheme();
|
|
22
|
+
const reduceMotion = useReducedMotion();
|
|
23
|
+
const opacity = useRef(new Animated.Value(reduceMotion ? 0.6 : 0.3)).current;
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (reduceMotion) {
|
|
26
|
+
opacity.setValue(0.6);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const animation = Animated.loop(Animated.sequence([
|
|
30
|
+
Animated.timing(opacity, {
|
|
31
|
+
toValue: 1,
|
|
32
|
+
duration: 800,
|
|
33
|
+
useNativeDriver: shouldUseNativeDriver,
|
|
34
|
+
}),
|
|
35
|
+
Animated.timing(opacity, {
|
|
36
|
+
toValue: 0.3,
|
|
37
|
+
duration: 800,
|
|
38
|
+
useNativeDriver: shouldUseNativeDriver,
|
|
39
|
+
}),
|
|
40
|
+
]));
|
|
41
|
+
animation.start();
|
|
42
|
+
return () => animation.stop();
|
|
43
|
+
}, [opacity, reduceMotion]);
|
|
44
|
+
const resolvedSize = circle ? (typeof height === "number" ? height : 40) : undefined;
|
|
45
|
+
return (_jsx(Animated.View, { style: [
|
|
46
|
+
{
|
|
47
|
+
width: circle ? resolvedSize : width,
|
|
48
|
+
height: circle ? resolvedSize : height,
|
|
49
|
+
borderRadius: circle ? (resolvedSize / 2) : borderRadius,
|
|
50
|
+
backgroundColor: theme.colors.muted,
|
|
51
|
+
opacity,
|
|
52
|
+
},
|
|
53
|
+
style,
|
|
54
|
+
] }));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* SkeletonText renders N horizontal bars simulating text.
|
|
58
|
+
* The last line is rendered at 60% width for a natural look.
|
|
59
|
+
*/
|
|
60
|
+
export function SkeletonText({ lines = 3, lineHeight = 14, gap = spacing.sm, style, }) {
|
|
61
|
+
return (_jsx(View, { style: [{ gap }, style], children: Array.from({ length: lines }).map((_, i) => (_jsx(Skeleton, { width: i === lines - 1 ? "60%" : "100%", height: lineHeight }, i))) }));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* SkeletonAvatar is a circular skeleton shorthand.
|
|
65
|
+
*/
|
|
66
|
+
export function SkeletonAvatar({ size = 40, style }) {
|
|
67
|
+
return _jsx(Skeleton, { width: size, height: size, circle: true, style: style });
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* SkeletonCard composes an image placeholder, optional avatar row,
|
|
71
|
+
* and text lines in a Card-styled wrapper.
|
|
72
|
+
*/
|
|
73
|
+
export function SkeletonCard({ showImage = true, imageHeight = 140, showAvatar = true, textLines = 3, style, }) {
|
|
74
|
+
const { theme } = useTheme();
|
|
75
|
+
const styles = createCardStyles(theme);
|
|
76
|
+
return (_jsxs(View, { style: [styles.card, style], children: [showImage && (_jsx(Skeleton, { width: "100%", height: imageHeight, borderRadius: 0 })), _jsxs(View, { style: styles.body, children: [showAvatar && (_jsxs(View, { style: styles.avatarRow, children: [_jsx(SkeletonAvatar, { size: 36 }), _jsxs(View, { style: styles.avatarText, children: [_jsx(Skeleton, { width: "50%", height: 14 }), _jsx(Skeleton, { width: "30%", height: 12 })] })] })), _jsx(SkeletonText, { lines: textLines })] })] }));
|
|
77
|
+
}
|
|
78
|
+
const createCardStyles = (theme) => StyleSheet.create({
|
|
79
|
+
card: {
|
|
80
|
+
backgroundColor: theme.colors.card,
|
|
81
|
+
borderRadius: spacing.radiusLg,
|
|
82
|
+
borderWidth: 1,
|
|
83
|
+
borderColor: theme.colors.border,
|
|
84
|
+
overflow: "hidden",
|
|
85
|
+
},
|
|
86
|
+
body: {
|
|
87
|
+
padding: spacing.md,
|
|
88
|
+
gap: spacing.md,
|
|
89
|
+
},
|
|
90
|
+
avatarRow: {
|
|
91
|
+
flexDirection: "row",
|
|
92
|
+
alignItems: "center",
|
|
93
|
+
gap: spacing.sm,
|
|
94
|
+
},
|
|
95
|
+
avatarText: {
|
|
96
|
+
flex: 1,
|
|
97
|
+
gap: spacing.xs,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { StyleProp, ViewStyle } from "react-native";
|
|
2
|
+
export type SliderSize = "sm" | "md";
|
|
3
|
+
export interface SliderProps {
|
|
4
|
+
/** Current value */
|
|
5
|
+
value?: number;
|
|
6
|
+
/** Called when the user drags the thumb */
|
|
7
|
+
onValueChange?: (value: number) => void;
|
|
8
|
+
/** Minimum value @default 0 */
|
|
9
|
+
min?: number;
|
|
10
|
+
/** Maximum value @default 100 */
|
|
11
|
+
max?: number;
|
|
12
|
+
/** Step increment @default 1 */
|
|
13
|
+
step?: number;
|
|
14
|
+
/** Size variant @default "md" */
|
|
15
|
+
size?: SliderSize;
|
|
16
|
+
/** Disable interaction @default false */
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
/** Show value label above thumb @default false */
|
|
19
|
+
showValue?: boolean;
|
|
20
|
+
/** Style override for outer container */
|
|
21
|
+
style?: StyleProp<ViewStyle>;
|
|
22
|
+
}
|
|
23
|
+
declare function Slider({ value, onValueChange, min, max, step, size, disabled, showValue, style: styleOverride, }: SliderProps): import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
export { Slider };
|