@robin-ux/native 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/.eslintrc.js +5 -0
- package/README.md +35 -0
- package/android/build.gradle +43 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/expo/modules/robinuxnative/RobinUxNativeModule.kt +50 -0
- package/android/src/main/java/expo/modules/robinuxnative/RobinUxNativeView.kt +30 -0
- package/build/RobinUxNative.types.d.ts +18 -0
- package/build/RobinUxNative.types.d.ts.map +1 -0
- package/build/RobinUxNative.types.js +2 -0
- package/build/RobinUxNative.types.js.map +1 -0
- package/build/RobinUxNativeModule.d.ts +10 -0
- package/build/RobinUxNativeModule.d.ts.map +1 -0
- package/build/RobinUxNativeModule.js +4 -0
- package/build/RobinUxNativeModule.js.map +1 -0
- package/build/RobinUxNativeModule.web.d.ts +10 -0
- package/build/RobinUxNativeModule.web.d.ts.map +1 -0
- package/build/RobinUxNativeModule.web.js +12 -0
- package/build/RobinUxNativeModule.web.js.map +1 -0
- package/build/RobinUxNativeView.d.ts +4 -0
- package/build/RobinUxNativeView.d.ts.map +1 -0
- package/build/RobinUxNativeView.js +7 -0
- package/build/RobinUxNativeView.js.map +1 -0
- package/build/RobinUxNativeView.web.d.ts +4 -0
- package/build/RobinUxNativeView.web.d.ts.map +1 -0
- package/build/RobinUxNativeView.web.js +7 -0
- package/build/RobinUxNativeView.web.js.map +1 -0
- package/build/components/Badge.d.ts +36 -0
- package/build/components/Badge.d.ts.map +1 -0
- package/build/components/Badge.js +78 -0
- package/build/components/Badge.js.map +1 -0
- package/build/components/Button.d.ts +43 -0
- package/build/components/Button.d.ts.map +1 -0
- package/build/components/Button.js +120 -0
- package/build/components/Button.js.map +1 -0
- package/build/components/DynamicStatusBar.d.ts +30 -0
- package/build/components/DynamicStatusBar.d.ts.map +1 -0
- package/build/components/DynamicStatusBar.js +70 -0
- package/build/components/DynamicStatusBar.js.map +1 -0
- package/build/components/Input.d.ts +73 -0
- package/build/components/Input.d.ts.map +1 -0
- package/build/components/Input.js +138 -0
- package/build/components/Input.js.map +1 -0
- package/build/components/SegmentedControl.d.ts +40 -0
- package/build/components/SegmentedControl.d.ts.map +1 -0
- package/build/components/SegmentedControl.js +73 -0
- package/build/components/SegmentedControl.js.map +1 -0
- package/build/components/Text.d.ts +32 -0
- package/build/components/Text.d.ts.map +1 -0
- package/build/components/Text.js +51 -0
- package/build/components/Text.js.map +1 -0
- package/build/components/index.d.ts +13 -0
- package/build/components/index.d.ts.map +1 -0
- package/build/components/index.js +13 -0
- package/build/components/index.js.map +1 -0
- package/build/index.d.ts +7 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +10 -0
- package/build/index.js.map +1 -0
- package/build/theme/ThemeContext.d.ts +22 -0
- package/build/theme/ThemeContext.d.ts.map +1 -0
- package/build/theme/ThemeContext.js +35 -0
- package/build/theme/ThemeContext.js.map +1 -0
- package/build/theme/ThemeProvider.d.ts +33 -0
- package/build/theme/ThemeProvider.d.ts.map +1 -0
- package/build/theme/ThemeProvider.js +31 -0
- package/build/theme/ThemeProvider.js.map +1 -0
- package/build/theme/defaultTheme.d.ts +18 -0
- package/build/theme/defaultTheme.d.ts.map +1 -0
- package/build/theme/defaultTheme.js +77 -0
- package/build/theme/defaultTheme.js.map +1 -0
- package/build/theme/index.d.ts +6 -0
- package/build/theme/index.d.ts.map +1 -0
- package/build/theme/index.js +7 -0
- package/build/theme/index.js.map +1 -0
- package/build/theme/types.d.ts +40 -0
- package/build/theme/types.d.ts.map +1 -0
- package/build/theme/types.js +2 -0
- package/build/theme/types.js.map +1 -0
- package/build/utils/index.d.ts +3 -0
- package/build/utils/index.d.ts.map +1 -0
- package/build/utils/index.js +3 -0
- package/build/utils/index.js.map +1 -0
- package/build/utils/styles.d.ts +88 -0
- package/build/utils/styles.d.ts.map +1 -0
- package/build/utils/styles.js +150 -0
- package/build/utils/styles.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/RobinUxNative.podspec +29 -0
- package/ios/RobinUxNativeModule.swift +48 -0
- package/ios/RobinUxNativeView.swift +38 -0
- package/package.json +69 -0
- package/src/RobinUxNative.types.ts +19 -0
- package/src/RobinUxNativeModule.ts +12 -0
- package/src/RobinUxNativeModule.web.ts +15 -0
- package/src/RobinUxNativeView.tsx +11 -0
- package/src/RobinUxNativeView.web.tsx +15 -0
- package/src/components/Badge.tsx +101 -0
- package/src/components/Button.tsx +176 -0
- package/src/components/DynamicStatusBar.tsx +93 -0
- package/src/components/Input.tsx +248 -0
- package/src/components/SegmentedControl.tsx +107 -0
- package/src/components/Text.tsx +107 -0
- package/src/components/index.ts +23 -0
- package/src/index.ts +70 -0
- package/src/theme/ThemeContext.ts +38 -0
- package/src/theme/ThemeProvider.tsx +45 -0
- package/src/theme/defaultTheme.ts +91 -0
- package/src/theme/index.ts +12 -0
- package/src/theme/types.ts +52 -0
- package/src/utils/index.ts +19 -0
- package/src/utils/styles.ts +188 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Animated } from 'react-native';
|
|
2
|
+
export interface DynamicStatusBarProps {
|
|
3
|
+
/** Animated scroll value to track */
|
|
4
|
+
scrollY: Animated.Value;
|
|
5
|
+
/** Scroll threshold for style change */
|
|
6
|
+
threshold?: number;
|
|
7
|
+
/** Default status bar style */
|
|
8
|
+
defaultStyle?: 'light' | 'dark';
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Dynamic status bar that changes style based on scroll position.
|
|
12
|
+
* Provides a smooth transition with animated background.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const scrollY = useRef(new Animated.Value(0)).current;
|
|
17
|
+
*
|
|
18
|
+
* <DynamicStatusBar scrollY={scrollY} threshold={200} />
|
|
19
|
+
* <Animated.ScrollView
|
|
20
|
+
* onScroll={Animated.event(
|
|
21
|
+
* [{ nativeEvent: { contentOffset: { y: scrollY } } }],
|
|
22
|
+
* { useNativeDriver: true }
|
|
23
|
+
* )}
|
|
24
|
+
* >
|
|
25
|
+
* {content}
|
|
26
|
+
* </Animated.ScrollView>
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function DynamicStatusBar({ scrollY, threshold, defaultStyle, }: DynamicStatusBarProps): import("react").JSX.Element;
|
|
30
|
+
//# sourceMappingURL=DynamicStatusBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DynamicStatusBar.d.ts","sourceRoot":"","sources":["../../src/components/DynamicStatusBar.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAc,MAAM,cAAc,CAAC;AAIpD,MAAM,WAAW,qBAAqB;IACpC,qCAAqC;IACrC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC;IACxB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,YAAY,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,SAAe,EACf,YAAsB,GACvB,EAAE,qBAAqB,+BA4CvB"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { StatusBar } from 'expo-status-bar';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { Animated, StyleSheet } from 'react-native';
|
|
4
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
5
|
+
import { useThemeSafe } from '../theme';
|
|
6
|
+
/**
|
|
7
|
+
* Dynamic status bar that changes style based on scroll position.
|
|
8
|
+
* Provides a smooth transition with animated background.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* const scrollY = useRef(new Animated.Value(0)).current;
|
|
13
|
+
*
|
|
14
|
+
* <DynamicStatusBar scrollY={scrollY} threshold={200} />
|
|
15
|
+
* <Animated.ScrollView
|
|
16
|
+
* onScroll={Animated.event(
|
|
17
|
+
* [{ nativeEvent: { contentOffset: { y: scrollY } } }],
|
|
18
|
+
* { useNativeDriver: true }
|
|
19
|
+
* )}
|
|
20
|
+
* >
|
|
21
|
+
* {content}
|
|
22
|
+
* </Animated.ScrollView>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function DynamicStatusBar({ scrollY, threshold = 200, defaultStyle = 'light', }) {
|
|
26
|
+
const { isDark, colors } = useThemeSafe();
|
|
27
|
+
const insets = useSafeAreaInsets();
|
|
28
|
+
const [style, setStyle] = useState(defaultStyle);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const listener = scrollY.addListener(({ value }) => {
|
|
31
|
+
if (isDark) {
|
|
32
|
+
if (style !== 'light')
|
|
33
|
+
setStyle('light');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const newStyle = value > threshold ? 'dark' : 'light';
|
|
37
|
+
if (newStyle !== style) {
|
|
38
|
+
setStyle(newStyle);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return () => scrollY.removeListener(listener);
|
|
42
|
+
}, [isDark, threshold, style, scrollY]);
|
|
43
|
+
const backgroundOpacity = scrollY.interpolate({
|
|
44
|
+
inputRange: [threshold - 40, threshold],
|
|
45
|
+
outputRange: [0, 1],
|
|
46
|
+
extrapolate: 'clamp',
|
|
47
|
+
});
|
|
48
|
+
const activeStyle = isDark ? 'light' : style;
|
|
49
|
+
return (<>
|
|
50
|
+
<StatusBar style={activeStyle}/>
|
|
51
|
+
<Animated.View style={[
|
|
52
|
+
styles.background,
|
|
53
|
+
{
|
|
54
|
+
height: insets.top,
|
|
55
|
+
backgroundColor: colors.background,
|
|
56
|
+
opacity: backgroundOpacity,
|
|
57
|
+
},
|
|
58
|
+
]}/>
|
|
59
|
+
</>);
|
|
60
|
+
}
|
|
61
|
+
const styles = StyleSheet.create({
|
|
62
|
+
background: {
|
|
63
|
+
position: 'absolute',
|
|
64
|
+
top: 0,
|
|
65
|
+
left: 0,
|
|
66
|
+
right: 0,
|
|
67
|
+
zIndex: 100,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=DynamicStatusBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DynamicStatusBar.js","sourceRoot":"","sources":["../../src/components/DynamicStatusBar.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAWxC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,OAAO,EACP,SAAS,GAAG,GAAG,EACf,YAAY,GAAG,OAAO,GACA;IACtB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,YAAY,EAAE,CAAC;IAC1C,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;IACnC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAmB,YAAY,CAAC,CAAC;IAEnE,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;YACjD,IAAI,MAAM,EAAE,CAAC;gBACX,IAAI,KAAK,KAAK,OAAO;oBAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;gBACzC,OAAO;YACT,CAAC;YAED,MAAM,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;YACtD,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;gBACvB,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACrB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;IAChD,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IAExC,MAAM,iBAAiB,GAAG,OAAO,CAAC,WAAW,CAAC;QAC5C,UAAU,EAAE,CAAC,SAAS,GAAG,EAAE,EAAE,SAAS,CAAC;QACvC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QACnB,WAAW,EAAE,OAAO;KACrB,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAE7C,OAAO,CACL,EACE;MAAA,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,EAC9B;MAAA,CAAC,QAAQ,CAAC,IAAI,CACZ,KAAK,CAAC,CAAC;YACL,MAAM,CAAC,UAAU;YACjB;gBACE,MAAM,EAAE,MAAM,CAAC,GAAG;gBAClB,eAAe,EAAE,MAAM,CAAC,UAAU;gBAClC,OAAO,EAAE,iBAAiB;aAC3B;SACF,CAAC,EAEN;IAAA,GAAG,CACJ,CAAC;AACJ,CAAC;AAED,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IAC/B,UAAU,EAAE;QACV,QAAQ,EAAE,UAAU;QACpB,GAAG,EAAE,CAAC;QACN,IAAI,EAAE,CAAC;QACP,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,GAAG;KACZ;CACF,CAAC,CAAC","sourcesContent":["import { StatusBar } from 'expo-status-bar';\nimport { useEffect, useState } from 'react';\nimport { Animated, StyleSheet } from 'react-native';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\nimport { useThemeSafe } from '../theme';\n\nexport interface DynamicStatusBarProps {\n /** Animated scroll value to track */\n scrollY: Animated.Value;\n /** Scroll threshold for style change */\n threshold?: number;\n /** Default status bar style */\n defaultStyle?: 'light' | 'dark';\n}\n\n/**\n * Dynamic status bar that changes style based on scroll position.\n * Provides a smooth transition with animated background.\n *\n * @example\n * ```tsx\n * const scrollY = useRef(new Animated.Value(0)).current;\n *\n * <DynamicStatusBar scrollY={scrollY} threshold={200} />\n * <Animated.ScrollView\n * onScroll={Animated.event(\n * [{ nativeEvent: { contentOffset: { y: scrollY } } }],\n * { useNativeDriver: true }\n * )}\n * >\n * {content}\n * </Animated.ScrollView>\n * ```\n */\nexport function DynamicStatusBar({\n scrollY,\n threshold = 200,\n defaultStyle = 'light',\n}: DynamicStatusBarProps) {\n const { isDark, colors } = useThemeSafe();\n const insets = useSafeAreaInsets();\n const [style, setStyle] = useState<'light' | 'dark'>(defaultStyle);\n\n useEffect(() => {\n const listener = scrollY.addListener(({ value }) => {\n if (isDark) {\n if (style !== 'light') setStyle('light');\n return;\n }\n\n const newStyle = value > threshold ? 'dark' : 'light';\n if (newStyle !== style) {\n setStyle(newStyle);\n }\n });\n\n return () => scrollY.removeListener(listener);\n }, [isDark, threshold, style, scrollY]);\n\n const backgroundOpacity = scrollY.interpolate({\n inputRange: [threshold - 40, threshold],\n outputRange: [0, 1],\n extrapolate: 'clamp',\n });\n\n const activeStyle = isDark ? 'light' : style;\n\n return (\n <>\n <StatusBar style={activeStyle} />\n <Animated.View\n style={[\n styles.background,\n {\n height: insets.top,\n backgroundColor: colors.background,\n opacity: backgroundOpacity,\n },\n ]}\n />\n </>\n );\n}\n\nconst styles = StyleSheet.create({\n background: {\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n zIndex: 100,\n },\n});\n"]}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Control } from 'react-hook-form';
|
|
3
|
+
import { TextInput, TextInputProps, ViewStyle } from 'react-native';
|
|
4
|
+
export interface InputProps extends Omit<TextInputProps, 'style'> {
|
|
5
|
+
/** Field label */
|
|
6
|
+
label?: string;
|
|
7
|
+
/** Error message to display */
|
|
8
|
+
error?: string;
|
|
9
|
+
/** Helper text below input */
|
|
10
|
+
helperText?: string;
|
|
11
|
+
/** Custom input style */
|
|
12
|
+
style?: TextInputProps['style'];
|
|
13
|
+
/** Container style */
|
|
14
|
+
containerStyle?: ViewStyle;
|
|
15
|
+
/** Input ref */
|
|
16
|
+
ref?: React.RefObject<TextInput | null>;
|
|
17
|
+
/** Element to render at the end of the input */
|
|
18
|
+
endElement?: React.ReactNode;
|
|
19
|
+
/** Child elements */
|
|
20
|
+
children?: React.ReactNode;
|
|
21
|
+
/** React Hook Form control object (optional) */
|
|
22
|
+
control?: Control<any>;
|
|
23
|
+
/** Field name for React Hook Form (optional) */
|
|
24
|
+
name?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Base input wrapper providing label, error, and helper text layout.
|
|
28
|
+
*/
|
|
29
|
+
export declare function BaseInput({ label, error, helperText, containerStyle, children, }: Pick<InputProps, 'label' | 'error' | 'helperText' | 'containerStyle' | 'children'>): React.JSX.Element;
|
|
30
|
+
/**
|
|
31
|
+
* Password input with visibility toggle.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* <PasswordInput
|
|
36
|
+
* label="Password"
|
|
37
|
+
* placeholder="Enter your password"
|
|
38
|
+
* value={password}
|
|
39
|
+
* onChangeText={setPassword}
|
|
40
|
+
* />
|
|
41
|
+
*
|
|
42
|
+
* // With React Hook Form
|
|
43
|
+
* <PasswordInput
|
|
44
|
+
* label="Password"
|
|
45
|
+
* control={control}
|
|
46
|
+
* name="password"
|
|
47
|
+
* />
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare const PasswordInput: React.FC<InputProps>;
|
|
51
|
+
/**
|
|
52
|
+
* Text input component with label, error, and helper text support.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```tsx
|
|
56
|
+
* <Input
|
|
57
|
+
* label="Email"
|
|
58
|
+
* placeholder="Enter your email"
|
|
59
|
+
* value={email}
|
|
60
|
+
* onChangeText={setEmail}
|
|
61
|
+
* keyboardType="email-address"
|
|
62
|
+
* />
|
|
63
|
+
*
|
|
64
|
+
* // With React Hook Form
|
|
65
|
+
* <Input
|
|
66
|
+
* label="Email"
|
|
67
|
+
* control={control}
|
|
68
|
+
* name="email"
|
|
69
|
+
* />
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export declare const Input: React.FC<InputProps>;
|
|
73
|
+
//# sourceMappingURL=Input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Input.d.ts","sourceRoot":"","sources":["../../src/components/Input.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,OAAO,EAAiB,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAQ,SAAS,EAAE,MAAM,cAAc,CAAC;AAM1E,MAAM,WAAW,UAAW,SAAQ,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC;IAC/D,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yBAAyB;IACzB,KAAK,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAChC,sBAAsB;IACtB,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B,gBAAgB;IAChB,GAAG,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IACxC,gDAAgD;IAChD,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,qBAAqB;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,gDAAgD;IAChD,OAAO,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IACvB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AASD;;GAEG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,KAAK,EACL,UAAU,EACV,cAAc,EACd,QAAQ,GACT,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,OAAO,GAAG,YAAY,GAAG,gBAAgB,GAAG,UAAU,CAAC,qBAgCpF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,UAAU,CAuE9C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,UAAU,CAoDtC,CAAC"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { useController } from 'react-hook-form';
|
|
3
|
+
import { TextInput, View } from 'react-native';
|
|
4
|
+
import { useThemeSafe } from '../theme';
|
|
5
|
+
import { borderRadius, spacing, useThemedStyles } from '../utils/styles';
|
|
6
|
+
import { Button } from './Button';
|
|
7
|
+
import { Text } from './Text';
|
|
8
|
+
const inputStyles = {
|
|
9
|
+
paddingHorizontal: spacing.md,
|
|
10
|
+
paddingVertical: spacing.md,
|
|
11
|
+
borderRadius: borderRadius.lg,
|
|
12
|
+
fontSize: 16,
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Base input wrapper providing label, error, and helper text layout.
|
|
16
|
+
*/
|
|
17
|
+
export function BaseInput({ label, error, helperText, containerStyle, children, }) {
|
|
18
|
+
const styles = useThemedStyles((colors) => ({
|
|
19
|
+
label: {
|
|
20
|
+
marginBottom: spacing.xs,
|
|
21
|
+
color: colors.foregroundSecondary,
|
|
22
|
+
fontSize: 14,
|
|
23
|
+
},
|
|
24
|
+
helperText: {
|
|
25
|
+
marginTop: spacing.xs,
|
|
26
|
+
},
|
|
27
|
+
}));
|
|
28
|
+
return (<View style={containerStyle}>
|
|
29
|
+
{label && (<Text variant="body" weight="medium" style={styles.label}>
|
|
30
|
+
{label}
|
|
31
|
+
</Text>)}
|
|
32
|
+
{children}
|
|
33
|
+
{error && (<Text variant="caption" color="error" style={styles.helperText}>
|
|
34
|
+
{error}
|
|
35
|
+
</Text>)}
|
|
36
|
+
{!error && helperText && (<Text variant="caption" color="secondary" style={styles.helperText}>
|
|
37
|
+
{helperText}
|
|
38
|
+
</Text>)}
|
|
39
|
+
</View>);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Password input with visibility toggle.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* <PasswordInput
|
|
47
|
+
* label="Password"
|
|
48
|
+
* placeholder="Enter your password"
|
|
49
|
+
* value={password}
|
|
50
|
+
* onChangeText={setPassword}
|
|
51
|
+
* />
|
|
52
|
+
*
|
|
53
|
+
* // With React Hook Form
|
|
54
|
+
* <PasswordInput
|
|
55
|
+
* label="Password"
|
|
56
|
+
* control={control}
|
|
57
|
+
* name="password"
|
|
58
|
+
* />
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export const PasswordInput = ({ value, onChangeText, style, onSubmitEditing, ref, error, control, name, ...props }) => {
|
|
62
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
63
|
+
const { colors } = useThemeSafe();
|
|
64
|
+
// Use useController if control and name are provided
|
|
65
|
+
const controller = control && name ? useController({ control, name }) : null;
|
|
66
|
+
const inputValue = controller ? controller.field.value : value;
|
|
67
|
+
const inputOnChange = controller ? controller.field.onChange : onChangeText;
|
|
68
|
+
const inputOnBlur = controller ? controller.field.onBlur : props.onBlur;
|
|
69
|
+
const inputRef = controller ? controller.field.ref : ref;
|
|
70
|
+
const hasError = error || controller?.fieldState.error;
|
|
71
|
+
return (<BaseInput label={props.label} error={error || controller?.fieldState.error?.message} helperText={props.helperText} containerStyle={props.containerStyle}>
|
|
72
|
+
<View style={[
|
|
73
|
+
{
|
|
74
|
+
backgroundColor: colors.backgroundSecondary,
|
|
75
|
+
borderColor: hasError ? colors.error : colors.border,
|
|
76
|
+
borderWidth: hasError ? 1 : 0,
|
|
77
|
+
flexDirection: 'row',
|
|
78
|
+
alignItems: 'center',
|
|
79
|
+
borderRadius: borderRadius.lg,
|
|
80
|
+
},
|
|
81
|
+
style,
|
|
82
|
+
]}>
|
|
83
|
+
<TextInput placeholderTextColor={colors.foregroundSecondary} ref={inputRef} {...props} style={[
|
|
84
|
+
inputStyles,
|
|
85
|
+
{
|
|
86
|
+
flex: 1,
|
|
87
|
+
color: colors.foreground,
|
|
88
|
+
},
|
|
89
|
+
]} value={inputValue} onChangeText={inputOnChange} autoCapitalize="none" onBlur={inputOnBlur} onSubmitEditing={onSubmitEditing} secureTextEntry={!showPassword}/>
|
|
90
|
+
<Button variant="ghost" icon={showPassword ? 'eye-off' : 'eye'} size="sm" onPress={() => setShowPassword(!showPassword)}/>
|
|
91
|
+
</View>
|
|
92
|
+
</BaseInput>);
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Text input component with label, error, and helper text support.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```tsx
|
|
99
|
+
* <Input
|
|
100
|
+
* label="Email"
|
|
101
|
+
* placeholder="Enter your email"
|
|
102
|
+
* value={email}
|
|
103
|
+
* onChangeText={setEmail}
|
|
104
|
+
* keyboardType="email-address"
|
|
105
|
+
* />
|
|
106
|
+
*
|
|
107
|
+
* // With React Hook Form
|
|
108
|
+
* <Input
|
|
109
|
+
* label="Email"
|
|
110
|
+
* control={control}
|
|
111
|
+
* name="email"
|
|
112
|
+
* />
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export const Input = ({ value, error, onChangeText, onSubmitEditing, style, ref, control, name, ...props }) => {
|
|
116
|
+
const { colors } = useThemeSafe();
|
|
117
|
+
// Use useController if control and name are provided
|
|
118
|
+
const controller = control && name ? useController({ control, name }) : null;
|
|
119
|
+
const inputValue = controller ? controller.field.value : value;
|
|
120
|
+
const inputOnChange = controller ? controller.field.onChange : onChangeText;
|
|
121
|
+
const inputOnBlur = controller ? controller.field.onBlur : props.onBlur;
|
|
122
|
+
const inputRef = controller ? controller.field.ref : ref;
|
|
123
|
+
const hasError = error || controller?.fieldState.error;
|
|
124
|
+
return (<BaseInput label={props.label} error={error || controller?.fieldState.error?.message} helperText={props.helperText} containerStyle={props.containerStyle}>
|
|
125
|
+
<TextInput placeholderTextColor={colors.foregroundSecondary} ref={inputRef} value={inputValue} onChangeText={inputOnChange} onBlur={inputOnBlur} onSubmitEditing={onSubmitEditing} {...props} style={[
|
|
126
|
+
inputStyles,
|
|
127
|
+
{
|
|
128
|
+
backgroundColor: colors.backgroundSecondary,
|
|
129
|
+
color: colors.foreground,
|
|
130
|
+
borderColor: hasError ? colors.error : colors.border,
|
|
131
|
+
borderWidth: hasError ? 1 : 0,
|
|
132
|
+
borderRadius: borderRadius.lg,
|
|
133
|
+
},
|
|
134
|
+
style,
|
|
135
|
+
]}/>
|
|
136
|
+
</BaseInput>);
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=Input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Input.js","sourceRoot":"","sources":["../../src/components/Input.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useState } from 'react';\nimport { Control, useController } from 'react-hook-form';\nimport { TextInput, TextInputProps, View, ViewStyle } from 'react-native';\nimport { useThemeSafe } from '../theme';\nimport { borderRadius, spacing, useThemedStyles } from '../utils/styles';\nimport { Button } from './Button';\nimport { Text } from './Text';\n\nexport interface InputProps extends Omit<TextInputProps, 'style'> {\n /** Field label */\n label?: string;\n /** Error message to display */\n error?: string;\n /** Helper text below input */\n helperText?: string;\n /** Custom input style */\n style?: TextInputProps['style'];\n /** Container style */\n containerStyle?: ViewStyle;\n /** Input ref */\n ref?: React.RefObject<TextInput | null>;\n /** Element to render at the end of the input */\n endElement?: React.ReactNode;\n /** Child elements */\n children?: React.ReactNode;\n /** React Hook Form control object (optional) */\n control?: Control<any>;\n /** Field name for React Hook Form (optional) */\n name?: string;\n}\n\nconst inputStyles = {\n paddingHorizontal: spacing.md,\n paddingVertical: spacing.md,\n borderRadius: borderRadius.lg,\n fontSize: 16,\n};\n\n/**\n * Base input wrapper providing label, error, and helper text layout.\n */\nexport function BaseInput({\n label,\n error,\n helperText,\n containerStyle,\n children,\n}: Pick<InputProps, 'label' | 'error' | 'helperText' | 'containerStyle' | 'children'>) {\n const styles = useThemedStyles((colors) => ({\n label: {\n marginBottom: spacing.xs,\n color: colors.foregroundSecondary,\n fontSize: 14,\n },\n helperText: {\n marginTop: spacing.xs,\n },\n }));\n\n return (\n <View style={containerStyle}>\n {label && (\n <Text variant=\"body\" weight=\"medium\" style={styles.label}>\n {label}\n </Text>\n )}\n {children}\n {error && (\n <Text variant=\"caption\" color=\"error\" style={styles.helperText}>\n {error}\n </Text>\n )}\n {!error && helperText && (\n <Text variant=\"caption\" color=\"secondary\" style={styles.helperText}>\n {helperText}\n </Text>\n )}\n </View>\n );\n}\n\n/**\n * Password input with visibility toggle.\n *\n * @example\n * ```tsx\n * <PasswordInput\n * label=\"Password\"\n * placeholder=\"Enter your password\"\n * value={password}\n * onChangeText={setPassword}\n * />\n *\n * // With React Hook Form\n * <PasswordInput\n * label=\"Password\"\n * control={control}\n * name=\"password\"\n * />\n * ```\n */\nexport const PasswordInput: React.FC<InputProps> = ({\n value,\n onChangeText,\n style,\n onSubmitEditing,\n ref,\n error,\n control,\n name,\n ...props\n}) => {\n const [showPassword, setShowPassword] = useState(false);\n const { colors } = useThemeSafe();\n\n // Use useController if control and name are provided\n const controller = control && name ? useController({ control, name }) : null;\n\n const inputValue = controller ? controller.field.value : value;\n const inputOnChange = controller ? controller.field.onChange : onChangeText;\n const inputOnBlur = controller ? controller.field.onBlur : props.onBlur;\n const inputRef = controller ? controller.field.ref : ref;\n\n const hasError = error || controller?.fieldState.error;\n\n return (\n <BaseInput\n label={props.label}\n error={error || controller?.fieldState.error?.message}\n helperText={props.helperText}\n containerStyle={props.containerStyle}\n >\n <View\n style={[\n {\n backgroundColor: colors.backgroundSecondary,\n borderColor: hasError ? colors.error : colors.border,\n borderWidth: hasError ? 1 : 0,\n flexDirection: 'row',\n alignItems: 'center',\n borderRadius: borderRadius.lg,\n },\n style as ViewStyle,\n ]}\n >\n <TextInput\n placeholderTextColor={colors.foregroundSecondary}\n ref={inputRef as any}\n {...props}\n style={[\n inputStyles,\n {\n flex: 1,\n color: colors.foreground,\n },\n ]}\n value={inputValue}\n onChangeText={inputOnChange}\n autoCapitalize=\"none\"\n onBlur={inputOnBlur}\n onSubmitEditing={onSubmitEditing}\n secureTextEntry={!showPassword}\n />\n <Button\n variant=\"ghost\"\n icon={showPassword ? 'eye-off' : 'eye'}\n size=\"sm\"\n onPress={() => setShowPassword(!showPassword)}\n />\n </View>\n </BaseInput>\n );\n};\n\n/**\n * Text input component with label, error, and helper text support.\n *\n * @example\n * ```tsx\n * <Input\n * label=\"Email\"\n * placeholder=\"Enter your email\"\n * value={email}\n * onChangeText={setEmail}\n * keyboardType=\"email-address\"\n * />\n *\n * // With React Hook Form\n * <Input\n * label=\"Email\"\n * control={control}\n * name=\"email\"\n * />\n * ```\n */\nexport const Input: React.FC<InputProps> = ({\n value,\n error,\n onChangeText,\n onSubmitEditing,\n style,\n ref,\n control,\n name,\n ...props\n}) => {\n const { colors } = useThemeSafe();\n\n // Use useController if control and name are provided\n const controller = control && name ? useController({ control, name }) : null;\n\n const inputValue = controller ? controller.field.value : value;\n const inputOnChange = controller ? controller.field.onChange : onChangeText;\n const inputOnBlur = controller ? controller.field.onBlur : props.onBlur;\n const inputRef = controller ? controller.field.ref : ref;\n\n const hasError = error || controller?.fieldState.error;\n\n return (\n <BaseInput\n label={props.label}\n error={error || controller?.fieldState.error?.message}\n helperText={props.helperText}\n containerStyle={props.containerStyle}\n >\n <TextInput\n placeholderTextColor={colors.foregroundSecondary}\n ref={inputRef as any}\n value={inputValue}\n onChangeText={inputOnChange}\n onBlur={inputOnBlur}\n onSubmitEditing={onSubmitEditing}\n {...props}\n style={[\n inputStyles,\n {\n backgroundColor: colors.backgroundSecondary,\n color: colors.foreground,\n borderColor: hasError ? colors.error : colors.border,\n borderWidth: hasError ? 1 : 0,\n borderRadius: borderRadius.lg,\n },\n style,\n ]}\n />\n </BaseInput>\n );\n};\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Ionicons } from "@expo/vector-icons";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { StyleProp, ViewStyle } from "react-native";
|
|
4
|
+
export interface Segment {
|
|
5
|
+
/** Unique value for this segment */
|
|
6
|
+
value: string;
|
|
7
|
+
/** Display label (optional if using icon only) */
|
|
8
|
+
label?: string;
|
|
9
|
+
/** Icon name from Ionicons (optional) */
|
|
10
|
+
icon?: keyof typeof Ionicons.glyphMap;
|
|
11
|
+
}
|
|
12
|
+
export interface SegmentedControlProps {
|
|
13
|
+
/** Array of segments to display */
|
|
14
|
+
segments: Segment[];
|
|
15
|
+
/** Currently selected segment value */
|
|
16
|
+
value: string;
|
|
17
|
+
/** Callback when selection changes */
|
|
18
|
+
onChange: (value: string) => void;
|
|
19
|
+
/** Control size */
|
|
20
|
+
size?: "sm" | "md";
|
|
21
|
+
/** Custom container style */
|
|
22
|
+
style?: StyleProp<ViewStyle>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Segmented control component for switching between options.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* <SegmentedControl
|
|
30
|
+
* segments={[
|
|
31
|
+
* { value: 'list', label: 'List', icon: 'list' },
|
|
32
|
+
* { value: 'grid', label: 'Grid', icon: 'grid' },
|
|
33
|
+
* ]}
|
|
34
|
+
* value={view}
|
|
35
|
+
* onChange={setView}
|
|
36
|
+
* />
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function SegmentedControl({ segments, value, onChange, size, style }: SegmentedControlProps): React.JSX.Element;
|
|
40
|
+
//# sourceMappingURL=SegmentedControl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SegmentedControl.d.ts","sourceRoot":"","sources":["../../src/components/SegmentedControl.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAa,SAAS,EAAQ,SAAS,EAAE,MAAM,cAAc,CAAC;AAKrE,MAAM,WAAW,OAAO;IACpB,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;CACzC;AAED,MAAM,WAAW,qBAAqB;IAClC,mCAAmC;IACnC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,mBAAmB;IACnB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,6BAA6B;IAC7B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAChC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAW,EAAE,KAAK,EAAE,EAAE,qBAAqB,qBA8DxG"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Ionicons } from "@expo/vector-icons";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Pressable, View } from "react-native";
|
|
4
|
+
import { useThemeSafe } from "../theme";
|
|
5
|
+
import { borderRadius, spacing, useThemedStyles } from "../utils/styles";
|
|
6
|
+
import { Text } from "./Text";
|
|
7
|
+
/**
|
|
8
|
+
* Segmented control component for switching between options.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <SegmentedControl
|
|
13
|
+
* segments={[
|
|
14
|
+
* { value: 'list', label: 'List', icon: 'list' },
|
|
15
|
+
* { value: 'grid', label: 'Grid', icon: 'grid' },
|
|
16
|
+
* ]}
|
|
17
|
+
* value={view}
|
|
18
|
+
* onChange={setView}
|
|
19
|
+
* />
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function SegmentedControl({ segments, value, onChange, size = "sm", style }) {
|
|
23
|
+
const { isDark } = useThemeSafe();
|
|
24
|
+
const styles = useThemedStyles((colors) => ({
|
|
25
|
+
container: {
|
|
26
|
+
backgroundColor: colors.backgroundSecondary,
|
|
27
|
+
borderRadius: borderRadius.full,
|
|
28
|
+
padding: spacing.xs,
|
|
29
|
+
flexDirection: "row",
|
|
30
|
+
alignSelf: "flex-start",
|
|
31
|
+
},
|
|
32
|
+
segment: {
|
|
33
|
+
alignItems: "center",
|
|
34
|
+
justifyContent: "center",
|
|
35
|
+
paddingVertical: spacing[size],
|
|
36
|
+
paddingHorizontal: spacing[size],
|
|
37
|
+
borderRadius: borderRadius.full,
|
|
38
|
+
flexDirection: "row",
|
|
39
|
+
gap: spacing[size],
|
|
40
|
+
},
|
|
41
|
+
activeSegment: {
|
|
42
|
+
backgroundColor: isDark ? colors.primaryLight : colors.primaryDark,
|
|
43
|
+
},
|
|
44
|
+
segmentText: {
|
|
45
|
+
color: colors.foregroundSecondary,
|
|
46
|
+
fontSize: size === "sm" ? 12 : 14,
|
|
47
|
+
fontWeight: "600",
|
|
48
|
+
},
|
|
49
|
+
segmentWithText: {
|
|
50
|
+
paddingHorizontal: size == "md" ? spacing.xl : spacing.lg,
|
|
51
|
+
},
|
|
52
|
+
activeSegmentText: {
|
|
53
|
+
color: colors.background,
|
|
54
|
+
},
|
|
55
|
+
segmentIcon: {
|
|
56
|
+
color: colors.foregroundSecondary,
|
|
57
|
+
fontSize: size === "sm" ? 14 : 16,
|
|
58
|
+
},
|
|
59
|
+
activeSegmentIcon: {
|
|
60
|
+
color: colors.background,
|
|
61
|
+
},
|
|
62
|
+
}));
|
|
63
|
+
return (<View style={[styles.container, style]}>
|
|
64
|
+
{segments.map((segment) => {
|
|
65
|
+
const isActive = segment.value === value;
|
|
66
|
+
return (<Pressable key={segment.value} style={[styles.segment, isActive && styles.activeSegment, segment.label && styles.segmentWithText]} onPress={() => onChange(segment.value)} accessibilityRole="radio" accessibilityState={{ selected: isActive }}>
|
|
67
|
+
{segment.icon && <Ionicons name={segment.icon} style={[styles.segmentIcon, isActive && styles.activeSegmentIcon]}/>}
|
|
68
|
+
{segment.label && <Text style={[styles.segmentText, isActive && styles.activeSegmentText]}>{segment.label}</Text>}
|
|
69
|
+
</Pressable>);
|
|
70
|
+
})}
|
|
71
|
+
</View>);
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=SegmentedControl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SegmentedControl.js","sourceRoot":"","sources":["../../src/components/SegmentedControl.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Ionicons } from \"@expo/vector-icons\";\nimport React from \"react\";\nimport { Pressable, StyleProp, View, ViewStyle } from \"react-native\";\nimport { useThemeSafe } from \"../theme\";\nimport { borderRadius, spacing, useThemedStyles } from \"../utils/styles\";\nimport { Text } from \"./Text\";\n\nexport interface Segment {\n /** Unique value for this segment */\n value: string;\n /** Display label (optional if using icon only) */\n label?: string;\n /** Icon name from Ionicons (optional) */\n icon?: keyof typeof Ionicons.glyphMap;\n}\n\nexport interface SegmentedControlProps {\n /** Array of segments to display */\n segments: Segment[];\n /** Currently selected segment value */\n value: string;\n /** Callback when selection changes */\n onChange: (value: string) => void;\n /** Control size */\n size?: \"sm\" | \"md\";\n /** Custom container style */\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * Segmented control component for switching between options.\n *\n * @example\n * ```tsx\n * <SegmentedControl\n * segments={[\n * { value: 'list', label: 'List', icon: 'list' },\n * { value: 'grid', label: 'Grid', icon: 'grid' },\n * ]}\n * value={view}\n * onChange={setView}\n * />\n * ```\n */\nexport function SegmentedControl({ segments, value, onChange, size = \"sm\", style }: SegmentedControlProps) {\n const { isDark } = useThemeSafe();\n\n const styles = useThemedStyles((colors) => ({\n container: {\n backgroundColor: colors.backgroundSecondary,\n borderRadius: borderRadius.full,\n padding: spacing.xs,\n flexDirection: \"row\",\n alignSelf: \"flex-start\",\n },\n segment: {\n alignItems: \"center\",\n justifyContent: \"center\",\n paddingVertical: spacing[size],\n paddingHorizontal: spacing[size],\n borderRadius: borderRadius.full,\n flexDirection: \"row\",\n gap: spacing[size],\n },\n activeSegment: {\n backgroundColor: isDark ? colors.primaryLight : colors.primaryDark,\n },\n segmentText: {\n color: colors.foregroundSecondary,\n fontSize: size === \"sm\" ? 12 : 14,\n fontWeight: \"600\",\n },\n segmentWithText: {\n paddingHorizontal: size == \"md\" ? spacing.xl : spacing.lg,\n },\n activeSegmentText: {\n color: colors.background,\n },\n segmentIcon: {\n color: colors.foregroundSecondary,\n fontSize: size === \"sm\" ? 14 : 16,\n },\n activeSegmentIcon: {\n color: colors.background,\n },\n }));\n\n return (\n <View style={[styles.container, style]}>\n {segments.map((segment) => {\n const isActive = segment.value === value;\n return (\n <Pressable\n key={segment.value}\n style={[styles.segment, isActive && styles.activeSegment, segment.label && styles.segmentWithText]}\n onPress={() => onChange(segment.value)}\n accessibilityRole=\"radio\"\n accessibilityState={{ selected: isActive }}\n >\n {segment.icon && <Ionicons name={segment.icon} style={[styles.segmentIcon, isActive && styles.activeSegmentIcon]} />}\n {segment.label && <Text style={[styles.segmentText, isActive && styles.activeSegmentText]}>{segment.label}</Text>}\n </Pressable>\n );\n })}\n </View>\n );\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TextProps as RNTextProps } from 'react-native';
|
|
3
|
+
export type TextVariant = 'heading' | 'subheading' | 'title' | 'body' | 'bodyMedium' | 'bodySemibold' | 'caption' | 'label';
|
|
4
|
+
export type TextWeight = 'regular' | 'medium' | 'semibold' | 'bold' | 'extrabold';
|
|
5
|
+
export type TextColor = 'primary' | 'secondary' | 'tertiary' | 'accent' | 'error' | 'success' | 'warning' | 'info' | 'white';
|
|
6
|
+
export type TextAlign = 'left' | 'center' | 'right' | 'justify';
|
|
7
|
+
export interface TextProps extends Omit<RNTextProps, 'style'> {
|
|
8
|
+
/** Typography variant */
|
|
9
|
+
variant?: TextVariant;
|
|
10
|
+
/** Font weight override */
|
|
11
|
+
weight?: TextWeight;
|
|
12
|
+
/** Text color */
|
|
13
|
+
color?: TextColor;
|
|
14
|
+
/** Text alignment */
|
|
15
|
+
align?: TextAlign;
|
|
16
|
+
/** Custom style */
|
|
17
|
+
style?: RNTextProps['style'];
|
|
18
|
+
/** Text content */
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Text component with semantic variants and theme support.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* <Text variant="heading" color="primary">Hello World</Text>
|
|
27
|
+
* <Text variant="body" weight="semibold">Bold text</Text>
|
|
28
|
+
* <Text variant="caption" color="secondary">Small helper text</Text>
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function Text({ variant, weight, color, align, style, children, ...props }: TextProps): React.JSX.Element;
|
|
32
|
+
//# sourceMappingURL=Text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Text.d.ts","sourceRoot":"","sources":["../../src/components/Text.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAkB,SAAS,IAAI,WAAW,EAAa,MAAM,cAAc,CAAC;AAInF,MAAM,MAAM,WAAW,GACnB,SAAS,GACT,YAAY,GACZ,OAAO,GACP,MAAM,GACN,YAAY,GACZ,cAAc,GACd,SAAS,GACT,OAAO,CAAC;AAEZ,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,WAAW,CAAC;AAElF,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,WAAW,GACX,UAAU,GACV,QAAQ,GACR,OAAO,GACP,SAAS,GACT,SAAS,GACT,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAEhE,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC;IAC3D,yBAAyB;IACzB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,iBAAiB;IACjB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,qBAAqB;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,mBAAmB;IACnB,KAAK,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAC7B,mBAAmB;IACnB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAWD;;;;;;;;;GASG;AACH,wBAAgB,IAAI,CAAC,EACnB,OAAgB,EAChB,MAAM,EACN,KAAiB,EACjB,KAAc,EACd,KAAK,EACL,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,SAAS,qBAkCX"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text as RNText } from 'react-native';
|
|
3
|
+
import { useThemeSafe } from '../theme';
|
|
4
|
+
import { typography } from '../utils/styles';
|
|
5
|
+
// Map weights to system font weights
|
|
6
|
+
const fontWeightMap = {
|
|
7
|
+
regular: '400',
|
|
8
|
+
medium: '500',
|
|
9
|
+
semibold: '600',
|
|
10
|
+
bold: '700',
|
|
11
|
+
extrabold: '800',
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Text component with semantic variants and theme support.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* <Text variant="heading" color="primary">Hello World</Text>
|
|
19
|
+
* <Text variant="body" weight="semibold">Bold text</Text>
|
|
20
|
+
* <Text variant="caption" color="secondary">Small helper text</Text>
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export function Text({ variant = 'body', weight, color = 'primary', align = 'left', style, children, ...props }) {
|
|
24
|
+
const { colors } = useThemeSafe();
|
|
25
|
+
// Get base variant style
|
|
26
|
+
const baseStyle = typography[variant];
|
|
27
|
+
// Determine font weight
|
|
28
|
+
const fontWeight = weight ? fontWeightMap[weight] : baseStyle.fontWeight;
|
|
29
|
+
// Get text color from theme
|
|
30
|
+
const textColor = {
|
|
31
|
+
primary: colors.foreground,
|
|
32
|
+
secondary: colors.foregroundSecondary,
|
|
33
|
+
tertiary: colors.foregroundTertiary,
|
|
34
|
+
accent: colors.accent,
|
|
35
|
+
error: colors.error,
|
|
36
|
+
success: colors.success,
|
|
37
|
+
warning: colors.warning,
|
|
38
|
+
info: colors.info,
|
|
39
|
+
white: '#ffffff',
|
|
40
|
+
};
|
|
41
|
+
const computedStyle = [
|
|
42
|
+
baseStyle,
|
|
43
|
+
{ color: textColor[color], textAlign: align },
|
|
44
|
+
fontWeight ? { fontWeight } : {},
|
|
45
|
+
style,
|
|
46
|
+
].filter(Boolean);
|
|
47
|
+
return (<RNText style={computedStyle} {...props}>
|
|
48
|
+
{children}
|
|
49
|
+
</RNText>);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=Text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Text.js","sourceRoot":"","sources":["../../src/components/Text.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,IAAI,IAAI,MAAM,EAAuC,MAAM,cAAc,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA0C7C,qCAAqC;AACrC,MAAM,aAAa,GAAgD;IACjE,OAAO,EAAE,KAAK;IACd,MAAM,EAAE,KAAK;IACb,QAAQ,EAAE,KAAK;IACf,IAAI,EAAE,KAAK;IACX,SAAS,EAAE,KAAK;CACjB,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,UAAU,IAAI,CAAC,EACnB,OAAO,GAAG,MAAM,EAChB,MAAM,EACN,KAAK,GAAG,SAAS,EACjB,KAAK,GAAG,MAAM,EACd,KAAK,EACL,QAAQ,EACR,GAAG,KAAK,EACE;IACV,MAAM,EAAE,MAAM,EAAE,GAAG,YAAY,EAAE,CAAC;IAElC,yBAAyB;IACzB,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAEtC,wBAAwB;IACxB,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC;IAEzE,4BAA4B;IAC5B,MAAM,SAAS,GAA8B;QAC3C,OAAO,EAAE,MAAM,CAAC,UAAU;QAC1B,SAAS,EAAE,MAAM,CAAC,mBAAmB;QACrC,QAAQ,EAAE,MAAM,CAAC,kBAAkB;QACnC,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,KAAK,EAAE,SAAS;KACjB,CAAC;IAEF,MAAM,aAAa,GAAgB;QACjC,SAAS;QACT,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE;QAC7C,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE;QAChC,KAAkB;KACnB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAElB,OAAO,CACL,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,CAAC,IAAI,KAAK,CAAC,CACtC;MAAA,CAAC,QAAQ,CACX;IAAA,EAAE,MAAM,CAAC,CACV,CAAC;AACJ,CAAC","sourcesContent":["import React from 'react';\nimport { Text as RNText, TextProps as RNTextProps, TextStyle } from 'react-native';\nimport { useThemeSafe } from '../theme';\nimport { typography } from '../utils/styles';\n\nexport type TextVariant =\n | 'heading'\n | 'subheading'\n | 'title'\n | 'body'\n | 'bodyMedium'\n | 'bodySemibold'\n | 'caption'\n | 'label';\n\nexport type TextWeight = 'regular' | 'medium' | 'semibold' | 'bold' | 'extrabold';\n\nexport type TextColor =\n | 'primary'\n | 'secondary'\n | 'tertiary'\n | 'accent'\n | 'error'\n | 'success'\n | 'warning'\n | 'info'\n | 'white';\n\nexport type TextAlign = 'left' | 'center' | 'right' | 'justify';\n\nexport interface TextProps extends Omit<RNTextProps, 'style'> {\n /** Typography variant */\n variant?: TextVariant;\n /** Font weight override */\n weight?: TextWeight;\n /** Text color */\n color?: TextColor;\n /** Text alignment */\n align?: TextAlign;\n /** Custom style */\n style?: RNTextProps['style'];\n /** Text content */\n children: React.ReactNode;\n}\n\n// Map weights to system font weights\nconst fontWeightMap: Record<TextWeight, TextStyle['fontWeight']> = {\n regular: '400',\n medium: '500',\n semibold: '600',\n bold: '700',\n extrabold: '800',\n};\n\n/**\n * Text component with semantic variants and theme support.\n *\n * @example\n * ```tsx\n * <Text variant=\"heading\" color=\"primary\">Hello World</Text>\n * <Text variant=\"body\" weight=\"semibold\">Bold text</Text>\n * <Text variant=\"caption\" color=\"secondary\">Small helper text</Text>\n * ```\n */\nexport function Text({\n variant = 'body',\n weight,\n color = 'primary',\n align = 'left',\n style,\n children,\n ...props\n}: TextProps) {\n const { colors } = useThemeSafe();\n\n // Get base variant style\n const baseStyle = typography[variant];\n\n // Determine font weight\n const fontWeight = weight ? fontWeightMap[weight] : baseStyle.fontWeight;\n\n // Get text color from theme\n const textColor: Record<TextColor, string> = {\n primary: colors.foreground,\n secondary: colors.foregroundSecondary,\n tertiary: colors.foregroundTertiary,\n accent: colors.accent,\n error: colors.error,\n success: colors.success,\n warning: colors.warning,\n info: colors.info,\n white: '#ffffff',\n };\n\n const computedStyle: TextStyle[] = [\n baseStyle,\n { color: textColor[color], textAlign: align },\n fontWeight ? { fontWeight } : {},\n style as TextStyle,\n ].filter(Boolean);\n\n return (\n <RNText style={computedStyle} {...props}>\n {children}\n </RNText>\n );\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { Button } from './Button';
|
|
2
|
+
export type { ButtonProps, ButtonVariant, ButtonSize } from './Button';
|
|
3
|
+
export { Text } from './Text';
|
|
4
|
+
export type { TextProps, TextVariant, TextWeight, TextColor, TextAlign } from './Text';
|
|
5
|
+
export { Input, PasswordInput, BaseInput } from './Input';
|
|
6
|
+
export type { InputProps } from './Input';
|
|
7
|
+
export { SegmentedControl } from './SegmentedControl';
|
|
8
|
+
export type { SegmentedControlProps, Segment } from './SegmentedControl';
|
|
9
|
+
export { Badge } from './Badge';
|
|
10
|
+
export type { BadgeProps, BadgeStatus } from './Badge';
|
|
11
|
+
export { DynamicStatusBar } from './DynamicStatusBar';
|
|
12
|
+
export type { DynamicStatusBarProps } from './DynamicStatusBar';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGvE,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAGvF,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC1D,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAG1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAGzE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Button
|
|
2
|
+
export { Button } from './Button';
|
|
3
|
+
// Text
|
|
4
|
+
export { Text } from './Text';
|
|
5
|
+
// Input
|
|
6
|
+
export { Input, PasswordInput, BaseInput } from './Input';
|
|
7
|
+
// SegmentedControl
|
|
8
|
+
export { SegmentedControl } from './SegmentedControl';
|
|
9
|
+
// Badge
|
|
10
|
+
export { Badge } from './Badge';
|
|
11
|
+
// DynamicStatusBar
|
|
12
|
+
export { DynamicStatusBar } from './DynamicStatusBar';
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,SAAS;AACT,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGlC,OAAO;AACP,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAG9B,QAAQ;AACR,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAG1D,mBAAmB;AACnB,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD,QAAQ;AACR,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGhC,mBAAmB;AACnB,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["// Button\nexport { Button } from './Button';\nexport type { ButtonProps, ButtonVariant, ButtonSize } from './Button';\n\n// Text\nexport { Text } from './Text';\nexport type { TextProps, TextVariant, TextWeight, TextColor, TextAlign } from './Text';\n\n// Input\nexport { Input, PasswordInput, BaseInput } from './Input';\nexport type { InputProps } from './Input';\n\n// SegmentedControl\nexport { SegmentedControl } from './SegmentedControl';\nexport type { SegmentedControlProps, Segment } from './SegmentedControl';\n\n// Badge\nexport { Badge } from './Badge';\nexport type { BadgeProps, BadgeStatus } from './Badge';\n\n// DynamicStatusBar\nexport { DynamicStatusBar } from './DynamicStatusBar';\nexport type { DynamicStatusBarProps } from './DynamicStatusBar';\n"]}
|