@mrmeg/expo-ui 0.13.0-rsd.0 → 0.13.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/CHANGELOG.md +31 -0
- package/LLM_USAGE.md +15 -0
- package/README.md +15 -1
- package/dist/components/Accordion.d.ts +18 -38
- package/dist/components/Accordion.js +121 -137
- package/dist/components/AnimatedView.d.ts +24 -13
- package/dist/components/AnimatedView.js +29 -70
- package/dist/components/Badge.d.ts +2 -2
- package/dist/components/Badge.js +47 -45
- package/dist/components/BottomSheet.d.ts +73 -30
- package/dist/components/BottomSheet.js +225 -191
- package/dist/components/Button.d.ts +56 -43
- package/dist/components/Button.js +203 -189
- package/dist/components/Card.d.ts +10 -7
- package/dist/components/Card.js +105 -90
- package/dist/components/Checkbox.d.ts +8 -20
- package/dist/components/Checkbox.js +80 -77
- package/dist/components/Collapsible.d.ts +47 -47
- package/dist/components/Collapsible.js +29 -108
- package/dist/components/Dialog.d.ts +121 -80
- package/dist/components/Dialog.js +132 -225
- package/dist/components/DismissKeyboard.js +2 -33
- package/dist/components/Drawer.d.ts +83 -37
- package/dist/components/Drawer.js +512 -252
- package/dist/components/DropdownMenu.d.ts +113 -106
- package/dist/components/DropdownMenu.js +204 -350
- package/dist/components/EmptyState.d.ts +2 -2
- package/dist/components/EmptyState.js +34 -41
- package/dist/components/InputOTP.d.ts +53 -19
- package/dist/components/InputOTP.js +102 -81
- package/dist/components/KeyboardAvoidingView.d.ts +3 -6
- package/dist/components/KeyboardAvoidingView.js +5 -18
- package/dist/components/Label.d.ts +17 -12
- package/dist/components/Label.js +51 -38
- package/dist/components/MaxWidthContainer.d.ts +16 -5
- package/dist/components/MaxWidthContainer.js +29 -28
- package/dist/components/Notification.d.ts +9 -5
- package/dist/components/Notification.js +187 -190
- package/dist/components/Popover.d.ts +66 -38
- package/dist/components/Popover.js +69 -158
- package/dist/components/Progress.d.ts +4 -5
- package/dist/components/Progress.js +77 -65
- package/dist/components/RadioGroup.d.ts +24 -30
- package/dist/components/RadioGroup.js +94 -90
- package/dist/components/Select.d.ts +74 -62
- package/dist/components/Select.js +154 -241
- package/dist/components/Separator.d.ts +23 -13
- package/dist/components/Separator.js +36 -29
- package/dist/components/Skeleton.d.ts +7 -8
- package/dist/components/Skeleton.js +61 -74
- package/dist/components/StyledText.context.d.ts +2 -6
- package/dist/components/StyledText.context.js +0 -3
- package/dist/components/StyledText.d.ts +7 -29
- package/dist/components/StyledText.js +29 -92
- package/dist/components/Switch.d.ts +6 -18
- package/dist/components/Switch.js +106 -112
- package/dist/components/Tabs.d.ts +16 -26
- package/dist/components/Tabs.js +91 -189
- package/dist/components/TextInput.d.ts +19 -6
- package/dist/components/TextInput.js +198 -226
- package/dist/components/Toggle.d.ts +41 -23
- package/dist/components/Toggle.js +98 -84
- package/dist/components/ToggleGroup.d.ts +29 -37
- package/dist/components/ToggleGroup.js +108 -113
- package/dist/components/Tooltip.d.ts +111 -41
- package/dist/components/Tooltip.js +118 -156
- package/dist/components/UIProvider.d.ts +1 -1
- package/dist/components/UIProvider.js +1 -1
- package/dist/components/keyboardFocusRegistry.d.ts +15 -1
- package/dist/components/keyboardFocusRegistry.js +39 -2
- package/dist/hooks/useTheme.d.ts +10 -34
- package/dist/hooks/useTheme.js +8 -20
- package/dist/lib/index.d.ts +0 -2
- package/dist/lib/index.js +0 -2
- package/package.json +29 -11
- package/dist/lib/portal.d.ts +0 -16
- package/dist/lib/portal.js +0 -84
- package/dist/lib/styles.d.ts +0 -32
- package/dist/lib/styles.js +0 -91
- package/dist/lib/useAnchoredPosition.d.ts +0 -57
- package/dist/lib/useAnchoredPosition.js +0 -120
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { ViewStyle, TextStyle, StyleProp } from "react-native";
|
|
2
3
|
import { TextProps } from "./StyledText";
|
|
3
|
-
import { type LooseStyle } from "../lib/styles";
|
|
4
4
|
export interface CardProps {
|
|
5
|
+
/** Card contents */
|
|
5
6
|
children?: React.ReactNode;
|
|
6
|
-
style
|
|
7
|
+
/** Custom style override */
|
|
8
|
+
style?: StyleProp<ViewStyle>;
|
|
9
|
+
/** Visual variant */
|
|
7
10
|
variant?: "default" | "outline" | "ghost";
|
|
8
11
|
/** Make card pressable with scale animation */
|
|
9
12
|
onPress?: () => void;
|
|
@@ -13,27 +16,27 @@ export interface CardProps {
|
|
|
13
16
|
declare function Card({ children, style: styleOverride, variant, onPress, disabled }: CardProps): React.JSX.Element;
|
|
14
17
|
export interface CardHeaderProps {
|
|
15
18
|
children?: React.ReactNode;
|
|
16
|
-
style?:
|
|
19
|
+
style?: StyleProp<ViewStyle>;
|
|
17
20
|
}
|
|
18
21
|
declare function CardHeader({ children, style: styleOverride }: CardHeaderProps): React.JSX.Element;
|
|
19
22
|
export interface CardContentProps {
|
|
20
23
|
children?: React.ReactNode;
|
|
21
|
-
style?:
|
|
24
|
+
style?: StyleProp<ViewStyle>;
|
|
22
25
|
}
|
|
23
26
|
declare function CardContent({ children, style: styleOverride }: CardContentProps): React.JSX.Element;
|
|
24
27
|
export interface CardFooterProps {
|
|
25
28
|
children?: React.ReactNode;
|
|
26
|
-
style?:
|
|
29
|
+
style?: StyleProp<ViewStyle>;
|
|
27
30
|
}
|
|
28
31
|
declare function CardFooter({ children, style: styleOverride }: CardFooterProps): React.JSX.Element;
|
|
29
32
|
export interface CardTitleProps extends Omit<TextProps, "style"> {
|
|
30
33
|
children?: React.ReactNode;
|
|
31
|
-
style?:
|
|
34
|
+
style?: StyleProp<TextStyle>;
|
|
32
35
|
}
|
|
33
36
|
declare function CardTitle({ children, style: styleOverride, ...props }: CardTitleProps): React.JSX.Element;
|
|
34
37
|
export interface CardDescriptionProps extends Omit<TextProps, "style"> {
|
|
35
38
|
children?: React.ReactNode;
|
|
36
|
-
style?:
|
|
39
|
+
style?: StyleProp<TextStyle>;
|
|
37
40
|
}
|
|
38
41
|
declare function CardDescription({ children, style: styleOverride, ...props }: CardDescriptionProps): React.JSX.Element;
|
|
39
42
|
export { Card, CardHeader, CardContent, CardFooter, CardTitle, CardDescription };
|
package/dist/components/Card.js
CHANGED
|
@@ -1,110 +1,125 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, use
|
|
3
|
-
import {
|
|
2
|
+
import { createContext, use } from "react";
|
|
3
|
+
import { View, Pressable, StyleSheet, Platform, Animated } from "react-native";
|
|
4
4
|
import { StyledText } from "./StyledText.js";
|
|
5
5
|
import { useTheme } from "../hooks/useTheme.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
transitionDuration: "0.1s",
|
|
40
|
-
},
|
|
41
|
-
pressed: { transform: "scale(0.98)" },
|
|
42
|
-
header: {
|
|
43
|
-
display: "flex",
|
|
44
|
-
flexDirection: "column",
|
|
45
|
-
padding: 24,
|
|
46
|
-
paddingBottom: 4,
|
|
47
|
-
gap: 4,
|
|
48
|
-
},
|
|
49
|
-
content: {
|
|
50
|
-
display: "flex",
|
|
51
|
-
flexDirection: "column",
|
|
52
|
-
paddingLeft: 24,
|
|
53
|
-
paddingRight: 24,
|
|
54
|
-
paddingBottom: 24,
|
|
55
|
-
},
|
|
56
|
-
footer: {
|
|
57
|
-
display: "flex",
|
|
58
|
-
flexDirection: "row",
|
|
59
|
-
alignItems: "center",
|
|
60
|
-
paddingLeft: 24,
|
|
61
|
-
paddingRight: 24,
|
|
62
|
-
paddingBottom: 24,
|
|
63
|
-
paddingTop: 0,
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
const TITLE_STYLE = { fontSize: 18, lineHeight: 24, letterSpacing: 0 };
|
|
67
|
-
const DESC_STYLE = { fontSize: 14, lineHeight: 20 };
|
|
6
|
+
import { useScalePress } from "../hooks/useScalePress.js";
|
|
7
|
+
import { spacing } from "../constants/spacing.js";
|
|
8
|
+
/**
|
|
9
|
+
* Card Component
|
|
10
|
+
*
|
|
11
|
+
* A themed container component with header, content, and footer sections.
|
|
12
|
+
* Follows shadcn/ui patterns with consistent styling and theme integration.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* ```tsx
|
|
16
|
+
* <Card>
|
|
17
|
+
* <CardHeader>
|
|
18
|
+
* <CardTitle>Card Title</CardTitle>
|
|
19
|
+
* <CardDescription>Card description text</CardDescription>
|
|
20
|
+
* </CardHeader>
|
|
21
|
+
* <CardContent>
|
|
22
|
+
* <Text>Main content goes here</Text>
|
|
23
|
+
* </CardContent>
|
|
24
|
+
* <CardFooter>
|
|
25
|
+
* <Button>Action</Button>
|
|
26
|
+
* </CardFooter>
|
|
27
|
+
* </Card>
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
const CardContext = createContext(null);
|
|
31
|
+
function useCardContext() {
|
|
32
|
+
const ctx = use(CardContext);
|
|
33
|
+
// useTheme must run unconditionally (Rules of Hooks); createCardStyles is a
|
|
34
|
+
// plain function, so the `??` keeps the fallback styles lazy.
|
|
35
|
+
const { theme } = useTheme();
|
|
36
|
+
// Fallback for standalone usage without a Card parent.
|
|
37
|
+
return ctx ?? { theme, styles: createCardStyles(theme) };
|
|
38
|
+
}
|
|
68
39
|
function Card({ children, style: styleOverride, variant = "default", onPress, disabled }) {
|
|
69
40
|
const { theme, getShadowStyle } = useTheme();
|
|
70
|
-
const
|
|
71
|
-
const [pressed, setPressed] = useState(false);
|
|
41
|
+
const styles = createCardStyles(theme);
|
|
72
42
|
const shadowStyle = getShadowStyle("subtle");
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
43
|
+
const ctx = { theme, styles };
|
|
44
|
+
const { animatedStyle: scaleStyle, pressHandlers } = useScalePress({
|
|
45
|
+
disabled: !onPress || !!disabled,
|
|
46
|
+
scaleTo: 0.98,
|
|
47
|
+
haptic: false,
|
|
48
|
+
});
|
|
49
|
+
const cardContent = (_jsx(View, { style: [
|
|
50
|
+
styles.card,
|
|
51
|
+
variant === "default" && styles.cardDefault,
|
|
52
|
+
variant === "default" && shadowStyle,
|
|
53
|
+
variant === "outline" && styles.cardOutline,
|
|
54
|
+
variant === "ghost" && styles.cardGhost,
|
|
55
|
+
styleOverride,
|
|
56
|
+
], children: children }));
|
|
82
57
|
if (onPress) {
|
|
83
|
-
return (_jsx(CardContext.Provider, { value:
|
|
84
|
-
onPress(); }, onPointerDown: () => !disabled && setPressed(true), onPointerUp: () => setPressed(false), onPointerLeave: () => setPressed(false), style: [
|
|
85
|
-
styles.pressable,
|
|
86
|
-
pressed && !disabled && !reduceMotion ? styles.pressed : null,
|
|
87
|
-
], children: cardContent }) }));
|
|
58
|
+
return (_jsx(CardContext.Provider, { value: ctx, children: _jsx(Pressable, { onPress: onPress, disabled: disabled, accessibilityRole: "button", accessibilityState: { disabled: !!disabled }, ...pressHandlers, style: Platform.OS === "web" ? { cursor: "pointer" } : undefined, children: _jsx(Animated.View, { style: scaleStyle, children: cardContent }) }) }));
|
|
88
59
|
}
|
|
89
|
-
return (_jsx(CardContext.Provider, { value:
|
|
60
|
+
return (_jsx(CardContext.Provider, { value: ctx, children: cardContent }));
|
|
90
61
|
}
|
|
91
62
|
function CardHeader({ children, style: styleOverride }) {
|
|
92
|
-
|
|
63
|
+
const { styles } = useCardContext();
|
|
64
|
+
return (_jsx(View, { style: [styles.header, styleOverride], children: children }));
|
|
93
65
|
}
|
|
94
66
|
function CardContent({ children, style: styleOverride }) {
|
|
95
|
-
|
|
67
|
+
const { styles } = useCardContext();
|
|
68
|
+
return (_jsx(View, { style: [styles.content, styleOverride], children: children }));
|
|
96
69
|
}
|
|
97
70
|
function CardFooter({ children, style: styleOverride }) {
|
|
98
|
-
|
|
71
|
+
const { styles } = useCardContext();
|
|
72
|
+
return (_jsx(View, { style: [styles.footer, styleOverride], children: children }));
|
|
99
73
|
}
|
|
100
74
|
function CardTitle({ children, style: styleOverride, ...props }) {
|
|
101
|
-
const { theme } =
|
|
102
|
-
|
|
103
|
-
return (_jsx(StyledText, { fontWeight: "semibold", ...props, style: [{ ...TITLE_STYLE, color: theme.colors.text }, styleOverride], children: children }));
|
|
75
|
+
const { theme, styles } = useCardContext();
|
|
76
|
+
return (_jsx(StyledText, { fontWeight: "semibold", ...props, style: [styles.title, { color: theme.colors.text }, styleOverride], children: children }));
|
|
104
77
|
}
|
|
105
78
|
function CardDescription({ children, style: styleOverride, ...props }) {
|
|
106
|
-
const { theme } =
|
|
107
|
-
|
|
108
|
-
return (_jsx(StyledText, { ...props, style: [{ ...DESC_STYLE, color: theme.colors.textDim }, styleOverride], children: children }));
|
|
79
|
+
const { theme, styles } = useCardContext();
|
|
80
|
+
return (_jsx(StyledText, { ...props, style: [styles.description, { color: theme.colors.textDim }, styleOverride], children: children }));
|
|
109
81
|
}
|
|
82
|
+
const createCardStyles = (theme) => StyleSheet.create({
|
|
83
|
+
card: {
|
|
84
|
+
borderRadius: spacing.radiusLg,
|
|
85
|
+
},
|
|
86
|
+
cardDefault: {
|
|
87
|
+
backgroundColor: theme.colors.card,
|
|
88
|
+
borderWidth: 1,
|
|
89
|
+
borderColor: theme.colors.border,
|
|
90
|
+
},
|
|
91
|
+
cardOutline: {
|
|
92
|
+
backgroundColor: "transparent",
|
|
93
|
+
borderWidth: 1,
|
|
94
|
+
borderColor: theme.colors.border,
|
|
95
|
+
},
|
|
96
|
+
cardGhost: {
|
|
97
|
+
backgroundColor: "transparent",
|
|
98
|
+
},
|
|
99
|
+
header: {
|
|
100
|
+
padding: spacing.lg,
|
|
101
|
+
paddingBottom: spacing.xs,
|
|
102
|
+
gap: spacing.xs,
|
|
103
|
+
},
|
|
104
|
+
content: {
|
|
105
|
+
paddingHorizontal: spacing.lg,
|
|
106
|
+
paddingBottom: spacing.lg,
|
|
107
|
+
},
|
|
108
|
+
footer: {
|
|
109
|
+
flexDirection: "row",
|
|
110
|
+
alignItems: "center",
|
|
111
|
+
paddingHorizontal: spacing.lg,
|
|
112
|
+
paddingBottom: spacing.lg,
|
|
113
|
+
paddingTop: 0,
|
|
114
|
+
},
|
|
115
|
+
title: {
|
|
116
|
+
fontSize: 18,
|
|
117
|
+
lineHeight: 24,
|
|
118
|
+
letterSpacing: 0,
|
|
119
|
+
},
|
|
120
|
+
description: {
|
|
121
|
+
fontSize: 14,
|
|
122
|
+
lineHeight: 20,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
110
125
|
export { Card, CardHeader, CardContent, CardFooter, CardTitle, CardDescription };
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { StyleProp, ViewStyle } from "react-native";
|
|
3
|
+
import * as CheckboxPrimitive from "@rn-primitives/checkbox";
|
|
3
4
|
/**
|
|
4
5
|
* Size variants for Checkbox
|
|
5
6
|
*/
|
|
6
7
|
export type CheckboxSize = "sm" | "md" | "lg";
|
|
7
|
-
export interface CheckboxProps {
|
|
8
|
-
/** Whether the checkbox is checked */
|
|
9
|
-
checked?: boolean;
|
|
10
|
-
/** Called with the next checked value when toggled */
|
|
11
|
-
onCheckedChange?: (checked: boolean) => void;
|
|
12
|
-
/** Whether the checkbox is disabled */
|
|
13
|
-
disabled?: boolean;
|
|
8
|
+
export interface CheckboxProps extends Omit<CheckboxPrimitive.RootProps, "style"> {
|
|
14
9
|
/**
|
|
15
10
|
* Size variant
|
|
16
11
|
* @default "md"
|
|
@@ -29,24 +24,17 @@ export interface CheckboxProps {
|
|
|
29
24
|
*/
|
|
30
25
|
error?: boolean;
|
|
31
26
|
/**
|
|
32
|
-
* Custom style override for
|
|
27
|
+
* Custom style override (uses StyleSheet.flatten for web compatibility)
|
|
33
28
|
*/
|
|
34
|
-
style?:
|
|
29
|
+
style?: StyleProp<ViewStyle>;
|
|
35
30
|
/**
|
|
36
|
-
* Style for the label
|
|
31
|
+
* Style for the label text
|
|
37
32
|
*/
|
|
38
|
-
labelStyle?:
|
|
33
|
+
labelStyle?: StyleProp<ViewStyle>;
|
|
39
34
|
/**
|
|
40
35
|
* Whether the field is required (shows asterisk in label)
|
|
41
36
|
*/
|
|
42
37
|
required?: boolean;
|
|
43
|
-
/** Forwarded accessibility label (defaults to `label`). */
|
|
44
|
-
"aria-label"?: string;
|
|
45
|
-
/** Accessibility label (RN-API alias of aria-label). */
|
|
46
|
-
accessibilityLabel?: string;
|
|
47
|
-
/** Forwarded test id (also accepts data-testid). */
|
|
48
|
-
testID?: string;
|
|
49
|
-
"data-testid"?: string;
|
|
50
38
|
}
|
|
51
|
-
declare function Checkbox({ size, label, indeterminate, error, style: styleOverride, labelStyle, required, checked, onCheckedChange, disabled,
|
|
39
|
+
declare function Checkbox({ size, label, indeterminate, error, style: styleOverride, labelStyle, required, checked, onCheckedChange, disabled, ...props }: CheckboxProps): React.JSX.Element;
|
|
52
40
|
export { Checkbox };
|
|
@@ -1,102 +1,105 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
3
|
+
import { View, StyleSheet, Pressable, Platform, Animated } from "react-native";
|
|
3
4
|
import { Icon } from "./Icon.js";
|
|
4
5
|
import { StyledText } from "./StyledText.js";
|
|
5
6
|
import { useTheme } from "../hooks/useTheme.js";
|
|
7
|
+
import { spacing } from "../constants/spacing.js";
|
|
6
8
|
import { hapticLight } from "../lib/haptics.js";
|
|
7
9
|
import { useReducedMotion } from "../hooks/useReduceMotion.js";
|
|
8
|
-
import
|
|
10
|
+
import * as CheckboxPrimitive from "@rn-primitives/checkbox";
|
|
11
|
+
const DEFAULT_HIT_SLOP = 8;
|
|
9
12
|
const SIZE_CONFIGS = {
|
|
10
|
-
sm: { size: 16, iconSize: 12 },
|
|
11
|
-
md: { size: 20, iconSize: 16 },
|
|
12
|
-
lg: { size: 24, iconSize: 20 },
|
|
13
|
+
sm: { size: 16, iconSize: 12, strokeWidth: 2.5 },
|
|
14
|
+
md: { size: 20, iconSize: 16, strokeWidth: 3 },
|
|
15
|
+
lg: { size: 24, iconSize: 20, strokeWidth: 3.5 },
|
|
13
16
|
};
|
|
14
|
-
|
|
15
|
-
// borderRadius literal (4) = spacing.radiusSm.
|
|
16
|
-
box: (size, borderColor, backgroundColor) => ({
|
|
17
|
-
width: size,
|
|
18
|
-
height: size,
|
|
19
|
-
borderRadius: 4,
|
|
20
|
-
borderWidth: 1,
|
|
21
|
-
borderStyle: "solid",
|
|
22
|
-
borderColor,
|
|
23
|
-
backgroundColor,
|
|
24
|
-
display: "flex",
|
|
25
|
-
alignItems: "center",
|
|
26
|
-
justifyContent: "center",
|
|
27
|
-
padding: 0,
|
|
28
|
-
margin: 0,
|
|
29
|
-
appearance: "none",
|
|
30
|
-
cursor: "pointer",
|
|
31
|
-
}),
|
|
32
|
-
disabledBox: { opacity: 0.5, cursor: "not-allowed" },
|
|
33
|
-
indicator: (opacity, animated) => ({
|
|
34
|
-
display: "flex",
|
|
35
|
-
alignItems: "center",
|
|
36
|
-
justifyContent: "center",
|
|
37
|
-
opacity,
|
|
38
|
-
transitionProperty: animated ? "opacity" : "none",
|
|
39
|
-
transitionDuration: animated ? "0.06s" : "0s",
|
|
40
|
-
}),
|
|
41
|
-
// minHeight literal (44) = spacing.touchTarget; gap (8) = spacing.sm.
|
|
42
|
-
container: {
|
|
43
|
-
display: "flex",
|
|
44
|
-
flexDirection: "row",
|
|
45
|
-
alignItems: "center",
|
|
46
|
-
gap: 8,
|
|
47
|
-
minHeight: 44,
|
|
48
|
-
appearance: "none",
|
|
49
|
-
backgroundColor: "transparent",
|
|
50
|
-
borderWidth: 0,
|
|
51
|
-
padding: 0,
|
|
52
|
-
margin: 0,
|
|
53
|
-
textAlign: "inherit",
|
|
54
|
-
cursor: "pointer",
|
|
55
|
-
},
|
|
56
|
-
disabledContainer: { cursor: "not-allowed" },
|
|
57
|
-
labelContainer: { flexGrow: 1, flexShrink: 1, flexBasis: "0%" },
|
|
58
|
-
});
|
|
59
|
-
const LABEL_TEXT_STYLE = { fontSize: 14 };
|
|
60
|
-
function Checkbox({ size = "md", label, indeterminate = false, error = false, style: styleOverride, labelStyle, required = false, checked, onCheckedChange, disabled, "aria-label": ariaLabelProp, accessibilityLabel, testID, "data-testid": dataTestId, }) {
|
|
17
|
+
function Checkbox({ size = "md", label, indeterminate = false, error = false, style: styleOverride, labelStyle, required = false, checked, onCheckedChange, disabled, ...props }) {
|
|
61
18
|
const { theme, getContrastingColor } = useTheme();
|
|
62
|
-
const ariaLabel = ariaLabelProp ?? accessibilityLabel;
|
|
63
19
|
const reduceMotion = useReducedMotion();
|
|
64
20
|
const sizeConfig = SIZE_CONFIGS[size];
|
|
21
|
+
// Simple fast opacity for the checkmark icon
|
|
22
|
+
const checkOpacity = useRef(new Animated.Value(checked || indeterminate ? 1 : 0)).current;
|
|
65
23
|
const isVisuallyChecked = !!checked || indeterminate;
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
24
|
+
const animateCheckOpacity = useCallback((nextVisible) => {
|
|
25
|
+
Animated.timing(checkOpacity, {
|
|
26
|
+
toValue: nextVisible ? 1 : 0,
|
|
27
|
+
duration: reduceMotion ? 0 : 60,
|
|
28
|
+
useNativeDriver: true,
|
|
29
|
+
}).start();
|
|
30
|
+
}, [checkOpacity, reduceMotion]);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
animateCheckOpacity(isVisuallyChecked);
|
|
33
|
+
}, [animateCheckOpacity, isVisuallyChecked]);
|
|
34
|
+
const wrappedOnCheckedChange = (next) => {
|
|
70
35
|
if (next)
|
|
71
36
|
hapticLight();
|
|
37
|
+
animateCheckOpacity(next);
|
|
72
38
|
onCheckedChange?.(next);
|
|
73
39
|
};
|
|
74
|
-
// Dynamic border color with sufficient contrast against
|
|
40
|
+
// Dynamic border color with sufficient contrast against background
|
|
75
41
|
const borderColor = error
|
|
76
42
|
? theme.colors.destructive
|
|
77
43
|
: isVisuallyChecked
|
|
78
44
|
? theme.colors.primary
|
|
79
45
|
: getContrastingColor(theme.colors.background, theme.colors.text, theme.colors.textDim);
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
46
|
+
// Flatten style override for web compatibility
|
|
47
|
+
const flattenedStyle = styleOverride ? StyleSheet.flatten(styleOverride) : undefined;
|
|
48
|
+
const checkboxElement = (_jsx(CheckboxPrimitive.Root, { ...props, checked: checked, onCheckedChange: wrappedOnCheckedChange, disabled: disabled, style: {
|
|
49
|
+
...styles.box,
|
|
50
|
+
borderColor,
|
|
51
|
+
backgroundColor: isVisuallyChecked ? theme.colors.primary : theme.colors.background,
|
|
52
|
+
width: sizeConfig.size,
|
|
53
|
+
height: sizeConfig.size,
|
|
54
|
+
opacity: disabled ? 0.5 : 1,
|
|
55
|
+
...(Platform.OS === "web" && { cursor: disabled ? "not-allowed" : "pointer" }),
|
|
56
|
+
...(flattenedStyle || {}),
|
|
57
|
+
}, hitSlop: DEFAULT_HIT_SLOP, accessibilityRole: "checkbox", accessibilityState: {
|
|
58
|
+
checked: indeterminate ? "mixed" : checked,
|
|
59
|
+
disabled: !!disabled,
|
|
60
|
+
}, accessibilityLabel: label, children: _jsx(CheckboxPrimitive.Indicator, { style: {
|
|
61
|
+
justifyContent: "center",
|
|
62
|
+
alignItems: "center",
|
|
63
|
+
}, children: _jsx(Animated.View, { style: { opacity: checkOpacity }, children: indeterminate ? (_jsx(Icon, { name: "minus", size: sizeConfig.iconSize, color: theme.colors.primaryForeground })) : (_jsx(Icon, { name: "check", size: sizeConfig.iconSize, color: theme.colors.primaryForeground })) }) }) }));
|
|
64
|
+
// If no label, return just the checkbox
|
|
88
65
|
if (!label) {
|
|
89
|
-
return
|
|
66
|
+
return checkboxElement;
|
|
90
67
|
}
|
|
91
|
-
// With
|
|
92
|
-
return (_jsxs(
|
|
93
|
-
|
|
94
|
-
disabled
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
68
|
+
// With label, wrap in a container
|
|
69
|
+
return (_jsxs(Pressable, { onPress: () => !disabled && wrappedOnCheckedChange(!checked), style: [styles.container, labelStyle], disabled: disabled, accessibilityRole: "checkbox", accessibilityState: {
|
|
70
|
+
checked: indeterminate ? "mixed" : checked,
|
|
71
|
+
disabled: !!disabled,
|
|
72
|
+
}, accessibilityLabel: label, children: [checkboxElement, _jsx(View, { style: styles.labelContainer, children: _jsxs(StyledText, { selectable: false, style: [
|
|
73
|
+
styles.label,
|
|
74
|
+
{ color: theme.colors.text },
|
|
75
|
+
disabled && styles.disabledLabel,
|
|
76
|
+
error && { color: theme.colors.destructive },
|
|
77
|
+
], children: [label, required && (_jsx(StyledText, { selectable: false, style: [styles.required, { color: theme.colors.destructive }], children: " *" }))] }) })] }));
|
|
101
78
|
}
|
|
79
|
+
const styles = StyleSheet.create({
|
|
80
|
+
box: {
|
|
81
|
+
borderRadius: spacing.radiusSm,
|
|
82
|
+
borderWidth: 1,
|
|
83
|
+
justifyContent: "center",
|
|
84
|
+
alignItems: "center",
|
|
85
|
+
},
|
|
86
|
+
container: {
|
|
87
|
+
flexDirection: "row",
|
|
88
|
+
alignItems: "center",
|
|
89
|
+
gap: spacing.sm,
|
|
90
|
+
minHeight: spacing.touchTarget,
|
|
91
|
+
},
|
|
92
|
+
labelContainer: {
|
|
93
|
+
flex: 1,
|
|
94
|
+
},
|
|
95
|
+
label: {
|
|
96
|
+
fontSize: 14,
|
|
97
|
+
},
|
|
98
|
+
disabledLabel: {
|
|
99
|
+
opacity: 0.5,
|
|
100
|
+
},
|
|
101
|
+
required: {
|
|
102
|
+
fontWeight: "bold",
|
|
103
|
+
},
|
|
104
|
+
});
|
|
102
105
|
export { Checkbox };
|
|
@@ -1,68 +1,68 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import
|
|
3
|
-
export interface CollapsibleProps {
|
|
4
|
-
/** Controlled open state. */
|
|
5
|
-
open?: boolean;
|
|
6
|
-
/** Uncontrolled initial open state. */
|
|
7
|
-
defaultOpen?: boolean;
|
|
8
|
-
/** Open-state change handler. */
|
|
9
|
-
onOpenChange?: (open: boolean) => void;
|
|
10
|
-
/** Disables the trigger. */
|
|
11
|
-
disabled?: boolean;
|
|
12
|
-
children?: React.ReactNode;
|
|
13
|
-
/** Custom style override for the root container. */
|
|
14
|
-
style?: LooseStyle;
|
|
15
|
-
}
|
|
2
|
+
import * as CollapsiblePrimitive from "@rn-primitives/collapsible";
|
|
16
3
|
/**
|
|
17
|
-
* Collapsible Component (
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* toggles a `shown` boolean shared via context; the content mounts only while
|
|
21
|
-
* open (or always, when `forceMount`).
|
|
4
|
+
* Collapsible Component (Root)
|
|
5
|
+
* A wrapper component that provides expand/collapse functionality with smooth animations
|
|
6
|
+
* Using @rn-primitives/collapsible with DaisyUI theme integration
|
|
22
7
|
*
|
|
23
8
|
* Usage:
|
|
24
9
|
* ```tsx
|
|
25
10
|
* const [open, setOpen] = useState(false);
|
|
26
11
|
* <Collapsible open={open} onOpenChange={setOpen}>
|
|
27
12
|
* <CollapsibleTrigger>
|
|
28
|
-
* <
|
|
13
|
+
* <SansSerifText>Toggle</SansSerifText>
|
|
29
14
|
* </CollapsibleTrigger>
|
|
30
15
|
* <CollapsibleContent>
|
|
31
|
-
* <
|
|
16
|
+
* <SansSerifText>Content that expands and collapses</SansSerifText>
|
|
32
17
|
* </CollapsibleContent>
|
|
33
18
|
* </Collapsible>
|
|
34
19
|
* ```
|
|
35
20
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
/** Render the single child as the trigger (clones it, injecting onPress). */
|
|
39
|
-
asChild?: boolean;
|
|
40
|
-
/** Disables this trigger only. */
|
|
41
|
-
disabled?: boolean;
|
|
42
|
-
children?: React.ReactNode;
|
|
43
|
-
/** Custom style override. */
|
|
44
|
-
style?: LooseStyle;
|
|
45
|
-
}
|
|
21
|
+
type CollapsibleProps = CollapsiblePrimitive.RootProps;
|
|
22
|
+
declare function Collapsible({ children, ...props }: CollapsibleProps): React.JSX.Element;
|
|
46
23
|
/**
|
|
47
24
|
* CollapsibleTrigger Component
|
|
48
|
-
* The element that toggles the collapsible state
|
|
49
|
-
*
|
|
25
|
+
* The trigger button/element that toggles the collapsible state
|
|
26
|
+
* Supports asChild pattern for custom trigger components
|
|
27
|
+
*
|
|
28
|
+
* Usage:
|
|
29
|
+
* ```tsx
|
|
30
|
+
* // Simple trigger
|
|
31
|
+
* <CollapsibleTrigger>
|
|
32
|
+
* <SansSerifText>Click to expand</SansSerifText>
|
|
33
|
+
* </CollapsibleTrigger>
|
|
34
|
+
*
|
|
35
|
+
* // With asChild (using Button)
|
|
36
|
+
* <CollapsibleTrigger asChild>
|
|
37
|
+
* <Button preset="outline">
|
|
38
|
+
* <SansSerifText>Toggle</SansSerifText>
|
|
39
|
+
* </Button>
|
|
40
|
+
* </CollapsibleTrigger>
|
|
41
|
+
* ```
|
|
50
42
|
*/
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Keep the content mounted (hidden) when closed instead of unmounting it.
|
|
55
|
-
* Default: false (content unmounts when closed).
|
|
56
|
-
*/
|
|
57
|
-
forceMount?: boolean;
|
|
58
|
-
children?: React.ReactNode;
|
|
59
|
-
/** Custom style override. */
|
|
60
|
-
style?: LooseStyle;
|
|
61
|
-
}
|
|
43
|
+
type CollapsibleTriggerProps = CollapsiblePrimitive.TriggerProps;
|
|
44
|
+
declare function CollapsibleTrigger({ style: styleOverride, ...props }: CollapsibleTriggerProps): React.JSX.Element;
|
|
62
45
|
/**
|
|
63
46
|
* CollapsibleContent Component
|
|
64
|
-
* The content that expands and collapses
|
|
65
|
-
*
|
|
47
|
+
* The content that expands and collapses
|
|
48
|
+
* Includes exit animation on native platforms
|
|
49
|
+
*
|
|
50
|
+
* Usage:
|
|
51
|
+
* ```tsx
|
|
52
|
+
* <CollapsibleContent>
|
|
53
|
+
* <View>
|
|
54
|
+
* <SansSerifText>This content will animate in and out</SansSerifText>
|
|
55
|
+
* </View>
|
|
56
|
+
* </CollapsibleContent>
|
|
57
|
+
* ```
|
|
66
58
|
*/
|
|
67
|
-
|
|
59
|
+
type CollapsibleContentProps = CollapsiblePrimitive.ContentProps & {
|
|
60
|
+
/**
|
|
61
|
+
* Whether to force remove the content from the tree when closed
|
|
62
|
+
* Default: false (content remains in tree but hidden)
|
|
63
|
+
*/
|
|
64
|
+
forceMount?: boolean;
|
|
65
|
+
};
|
|
66
|
+
declare function CollapsibleContent({ forceMount, style: styleOverride, children, ...props }: CollapsibleContentProps): React.JSX.Element;
|
|
68
67
|
export { Collapsible, CollapsibleContent, CollapsibleTrigger };
|
|
68
|
+
export type { CollapsibleContentProps, CollapsibleProps, CollapsibleTriggerProps };
|