ripal-ui 1.1.395 → 2.0.2
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 +3 -0
- package/components/Alert.jsx +74 -0
- package/components/Avatar.jsx +144 -0
- package/components/BottomSheet.jsx +187 -0
- package/components/Breadcrumb.jsx +48 -0
- package/components/Button.jsx +58 -0
- package/components/COLORS.js +138 -0
- package/components/Card.jsx +33 -0
- package/components/Checkbox.jsx +39 -0
- package/components/Dialog.jsx +188 -0
- package/components/Divider.jsx +62 -0
- package/components/Dropdown.jsx +229 -0
- package/components/Grid.jsx +26 -0
- package/components/Inline.jsx +37 -0
- package/components/Input.jsx +89 -0
- package/components/ProgressBar.jsx +65 -0
- package/components/Rate.jsx +39 -0
- package/components/Slider.jsx +219 -0
- package/components/Switch.jsx +45 -0
- package/components/Table.jsx +67 -0
- package/components/Text.jsx +56 -0
- package/components/Toggle.jsx +88 -0
- package/index.js +21 -2
- package/package.json +19 -23
- package/babel.config.js +0 -3
- package/components/BottomSheet.tsx +0 -197
- package/components/Carousel.tsx +0 -61
- package/components/Circle.tsx +0 -44
- package/components/DatePicker.jsx +0 -181
- package/components/Tab.tsx +0 -90
- package/components/Table.tsx +0 -95
- package/components/index.ts +0 -5
- package/config.js +0 -4
- package/dist/BottomSheet.js +0 -186
- package/dist/Button.js +0 -109
- package/dist/Carousel.js +0 -52
- package/dist/Circle.js +0 -42
- package/dist/DatePicker.js +0 -199
- package/dist/Dialog.js +0 -81
- package/dist/Dropdown.js +0 -97
- package/dist/Inline.js +0 -38
- package/dist/Input.js +0 -88
- package/dist/ProgressBar.js +0 -64
- package/dist/Separator.js +0 -47
- package/dist/Skeleton.js +0 -62
- package/dist/Switch.js +0 -74
- package/dist/Tab.js +0 -85
- package/dist/Table.js +0 -96
- package/dist/Text.js +0 -78
- package/dist/Toast.js +0 -72
- package/dist/Toggle.js +0 -54
- package/dist/index.js +0 -96
- package/elements/Button.tsx +0 -121
- package/elements/ColorPicker.tsx +0 -70
- package/elements/Dialog.tsx +0 -87
- package/elements/Dropdown.tsx +0 -88
- package/elements/Inline.tsx +0 -52
- package/elements/Input.tsx +0 -83
- package/elements/ProgressBar.tsx +0 -52
- package/elements/SecureStorage.js +0 -27
- package/elements/Separator.tsx +0 -71
- package/elements/Skeleton.tsx +0 -64
- package/elements/Slider.tsx +0 -133
- package/elements/Switch.tsx +0 -63
- package/elements/Text.tsx +0 -73
- package/elements/Toast.tsx +0 -71
- package/elements/Toggle.tsx +0 -59
- package/elements/index.js +0 -14
- package/index.d.ts +0 -237
- package/scripts/generateConfig.js +0 -80
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { StyleSheet, TouchableOpacity } from "react-native";
|
|
3
|
+
import COLORS from "./COLORS";
|
|
4
|
+
import MaterialIcons from "@react-native-vector-icons/material-icons";
|
|
5
|
+
|
|
6
|
+
const Checkbox = ({size = 20, active, color = COLORS.primary, setActive, icon = null, iconSize = 14}) => {
|
|
7
|
+
return (
|
|
8
|
+
<TouchableOpacity style={[
|
|
9
|
+
styles.box,
|
|
10
|
+
{
|
|
11
|
+
height: size,
|
|
12
|
+
borderColor: active ? color : COLORS.slate[300],
|
|
13
|
+
backgroundColor: active ? color : '#ffffff00'
|
|
14
|
+
}
|
|
15
|
+
]} onPress={() => {
|
|
16
|
+
setActive(!active);
|
|
17
|
+
}}>
|
|
18
|
+
{
|
|
19
|
+
active &&
|
|
20
|
+
<>
|
|
21
|
+
{ icon !== null ? icon :
|
|
22
|
+
<MaterialIcons name="check" size={iconSize} color={'#fff'} />}
|
|
23
|
+
</>
|
|
24
|
+
}
|
|
25
|
+
</TouchableOpacity>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const styles = StyleSheet.create({
|
|
30
|
+
box: {
|
|
31
|
+
borderWidth: 1,
|
|
32
|
+
borderRadius: 6,
|
|
33
|
+
aspectRatio: 1,
|
|
34
|
+
alignItems: 'center',
|
|
35
|
+
justifyContent: 'center'
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
export default Checkbox;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import React, { createContext, useContext, useEffect, useState } from "react";
|
|
2
|
+
import { Keyboard, KeyboardAvoidingView, Modal, Platform, Pressable, StyleSheet, TouchableWithoutFeedback, View } from "react-native";
|
|
3
|
+
import Text from "./Text";
|
|
4
|
+
import Inline from "./Inline";
|
|
5
|
+
import COLORS from "./COLORS";
|
|
6
|
+
import Button from "./Button";
|
|
7
|
+
import MaterialIcons from "@react-native-vector-icons/material-icons";
|
|
8
|
+
|
|
9
|
+
const DialogContext = createContext(null);
|
|
10
|
+
|
|
11
|
+
const useDialog = () => {
|
|
12
|
+
const ctx = useContext(DialogContext);
|
|
13
|
+
if (!ctx) throw new Error("Dialog subcomponents must be used inside Dialog");
|
|
14
|
+
return ctx;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const Dialog = ({
|
|
18
|
+
children,
|
|
19
|
+
visible = false,
|
|
20
|
+
onClose,
|
|
21
|
+
setVisible = null,
|
|
22
|
+
keyboardOffset = 0
|
|
23
|
+
}) => {
|
|
24
|
+
|
|
25
|
+
const [isVisible, setIsVisible] = useState(visible);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
setIsVisible(visible);
|
|
29
|
+
}, [visible]);
|
|
30
|
+
|
|
31
|
+
const closeHandler = () => {
|
|
32
|
+
const next = false;
|
|
33
|
+
|
|
34
|
+
if (onClose) onClose();
|
|
35
|
+
if (setVisible) setVisible(next);
|
|
36
|
+
|
|
37
|
+
setIsVisible(next);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<DialogContext.Provider
|
|
42
|
+
value={{
|
|
43
|
+
visible: isVisible,
|
|
44
|
+
close: closeHandler,
|
|
45
|
+
setVisible
|
|
46
|
+
}}
|
|
47
|
+
>
|
|
48
|
+
<Modal
|
|
49
|
+
transparent
|
|
50
|
+
visible={isVisible}
|
|
51
|
+
onRequestClose={closeHandler}
|
|
52
|
+
statusBarTranslucent
|
|
53
|
+
animationType="fade"
|
|
54
|
+
>
|
|
55
|
+
<KeyboardAvoidingView
|
|
56
|
+
style={styles.wrapper}
|
|
57
|
+
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
|
58
|
+
keyboardVerticalOffset={keyboardOffset}
|
|
59
|
+
>
|
|
60
|
+
|
|
61
|
+
{/* tap outside dialog */}
|
|
62
|
+
<Pressable
|
|
63
|
+
style={StyleSheet.absoluteFill}
|
|
64
|
+
onPress={closeHandler}
|
|
65
|
+
/>
|
|
66
|
+
|
|
67
|
+
{/* dismiss keyboard when tap inside empty area */}
|
|
68
|
+
<TouchableWithoutFeedback onPress={() => {
|
|
69
|
+
Keyboard.dismiss();
|
|
70
|
+
closeHandler();
|
|
71
|
+
}}>
|
|
72
|
+
<View style={styles.center}>
|
|
73
|
+
<Pressable
|
|
74
|
+
style={styles.container}
|
|
75
|
+
onPress={(e) => e.stopPropagation()}
|
|
76
|
+
>
|
|
77
|
+
{children}
|
|
78
|
+
</Pressable>
|
|
79
|
+
</View>
|
|
80
|
+
</TouchableWithoutFeedback>
|
|
81
|
+
|
|
82
|
+
</KeyboardAvoidingView>
|
|
83
|
+
</Modal>
|
|
84
|
+
</DialogContext.Provider>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
Dialog.Body = ({ children, gap = 20 }) => {
|
|
89
|
+
const { close } = useDialog();
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<View style={{padding: 20, gap: gap }}>
|
|
93
|
+
{children}
|
|
94
|
+
</View>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
Dialog.Title = ({ children, withClose = true }) => {
|
|
99
|
+
const { close } = useDialog();
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<Inline style={{padding: 20}}>
|
|
103
|
+
{
|
|
104
|
+
typeof children === "string"
|
|
105
|
+
? (
|
|
106
|
+
<Text
|
|
107
|
+
size={20}
|
|
108
|
+
color={COLORS.slate[800]}
|
|
109
|
+
style={styles.title_text}
|
|
110
|
+
>
|
|
111
|
+
{children}
|
|
112
|
+
</Text>
|
|
113
|
+
)
|
|
114
|
+
: (
|
|
115
|
+
<View style={{ gap: 5, flexGrow: 1 }}>
|
|
116
|
+
{children}
|
|
117
|
+
</View>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
{withClose && (
|
|
122
|
+
<Button
|
|
123
|
+
circle
|
|
124
|
+
color={`${COLORS.slate[200]}aa`}
|
|
125
|
+
onPress={close}
|
|
126
|
+
>
|
|
127
|
+
<MaterialIcons name="close" />
|
|
128
|
+
</Button>
|
|
129
|
+
)}
|
|
130
|
+
</Inline>
|
|
131
|
+
);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
Dialog.Actions = ({ children, gap = 10 }) => {
|
|
135
|
+
return (
|
|
136
|
+
<Inline style={[styles.action_area]} gap={gap}>
|
|
137
|
+
{children}
|
|
138
|
+
</Inline>
|
|
139
|
+
);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const styles = StyleSheet.create({
|
|
143
|
+
wrapperss: {
|
|
144
|
+
alignItems: 'center',
|
|
145
|
+
justifyContent: 'center',
|
|
146
|
+
flexGrow: 1,
|
|
147
|
+
position: 'absolute',
|
|
148
|
+
top: 0,left: 0,right: 0,bottom: 0,
|
|
149
|
+
backgroundColor: '#00000080',
|
|
150
|
+
padding: 20,
|
|
151
|
+
},
|
|
152
|
+
wrapper: {
|
|
153
|
+
flex: 1,
|
|
154
|
+
backgroundColor: '#00000080',
|
|
155
|
+
},
|
|
156
|
+
center: {
|
|
157
|
+
flex: 1,
|
|
158
|
+
justifyContent: "center",
|
|
159
|
+
alignItems: "center",
|
|
160
|
+
padding: 20,
|
|
161
|
+
},
|
|
162
|
+
container: {
|
|
163
|
+
backgroundColor: "#fff",
|
|
164
|
+
borderRadius: 14,
|
|
165
|
+
width: "100%",
|
|
166
|
+
maxWidth: 500,
|
|
167
|
+
},
|
|
168
|
+
containerss: {
|
|
169
|
+
backgroundColor: '#fff',
|
|
170
|
+
padding: 0,
|
|
171
|
+
flexGrow: 1,
|
|
172
|
+
borderRadius: 14,
|
|
173
|
+
},
|
|
174
|
+
title_text: {
|
|
175
|
+
fontWeight: '700',
|
|
176
|
+
flexGrow: 1,
|
|
177
|
+
},
|
|
178
|
+
action_area: {
|
|
179
|
+
padding: 20,
|
|
180
|
+
justifyContent: 'flex-end',
|
|
181
|
+
backgroundColor: COLORS.slate[100],
|
|
182
|
+
borderBottomLeftRadius: 14,
|
|
183
|
+
borderBottomRightRadius: 14,
|
|
184
|
+
marginTop: 20,
|
|
185
|
+
}
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
export default Dialog;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { StyleSheet, TouchableOpacity, View } from "react-native";
|
|
3
|
+
import Text from "./Text";
|
|
4
|
+
import Inline from "./Inline";
|
|
5
|
+
import COLORS from "./COLORS";
|
|
6
|
+
|
|
7
|
+
const Divider = ({label = null, labelBackgroundColor = '#fff', labelStyle, labelPosition = "center", labelColor = COLORS.slate[500], height = 0.5, color = COLORS.slate[300]}) => {
|
|
8
|
+
const positionToFlex = {
|
|
9
|
+
left: "flex-start",
|
|
10
|
+
center: "center",
|
|
11
|
+
right: "flex-end"
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Inline style={styles.container}>
|
|
16
|
+
<View style={[
|
|
17
|
+
styles.line,
|
|
18
|
+
{
|
|
19
|
+
backgroundColor: color,
|
|
20
|
+
height,
|
|
21
|
+
}
|
|
22
|
+
]} />
|
|
23
|
+
{
|
|
24
|
+
label !== null &&
|
|
25
|
+
<Inline style={styles.label_container} justifyContent={positionToFlex[labelPosition.toLowerCase()]}>
|
|
26
|
+
<Inline style={[
|
|
27
|
+
styles.label_area,
|
|
28
|
+
{
|
|
29
|
+
backgroundColor: labelBackgroundColor,
|
|
30
|
+
},
|
|
31
|
+
labelStyle
|
|
32
|
+
]}>
|
|
33
|
+
{ typeof label === "string" ?
|
|
34
|
+
<Text color={labelColor} size={12}>{label}</Text>
|
|
35
|
+
:
|
|
36
|
+
label
|
|
37
|
+
}
|
|
38
|
+
</Inline>
|
|
39
|
+
</Inline>
|
|
40
|
+
}
|
|
41
|
+
</Inline>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const styles = StyleSheet.create({
|
|
46
|
+
container: {
|
|
47
|
+
position: 'relative'
|
|
48
|
+
},
|
|
49
|
+
line: {
|
|
50
|
+
flexGrow: 1,
|
|
51
|
+
},
|
|
52
|
+
label_area: {
|
|
53
|
+
padding: 5,
|
|
54
|
+
paddingHorizontal: 15,
|
|
55
|
+
},
|
|
56
|
+
label_container: {
|
|
57
|
+
position: 'absolute',
|
|
58
|
+
left: 0,right: 0,
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
export default Divider;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import React, { useRef, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Pressable,
|
|
5
|
+
ScrollView,
|
|
6
|
+
StyleSheet,
|
|
7
|
+
TouchableOpacity,
|
|
8
|
+
Modal,
|
|
9
|
+
Dimensions
|
|
10
|
+
} from "react-native";
|
|
11
|
+
import { MaterialIcons } from "@react-native-vector-icons/material-icons";
|
|
12
|
+
import Inline from "./Inline";
|
|
13
|
+
import Text from "./Text";
|
|
14
|
+
import COLORS from "./COLORS";
|
|
15
|
+
|
|
16
|
+
const Dropdown = ({
|
|
17
|
+
label = "Pick one",
|
|
18
|
+
options = [],
|
|
19
|
+
objectKey = null,
|
|
20
|
+
keyExtractor = null,
|
|
21
|
+
value = null,
|
|
22
|
+
onChange = () => {},
|
|
23
|
+
labelStyle,
|
|
24
|
+
style,
|
|
25
|
+
inputStyle,
|
|
26
|
+
}) => {
|
|
27
|
+
|
|
28
|
+
const triggerRef = useRef(null);
|
|
29
|
+
const [visible, setVisible] = useState(false);
|
|
30
|
+
const [layout, setLayout] = useState(null);
|
|
31
|
+
|
|
32
|
+
const screenHeight = Dimensions.get("window").height;
|
|
33
|
+
|
|
34
|
+
const getLabel = (item) => {
|
|
35
|
+
if (item === null || item === undefined) return "";
|
|
36
|
+
if (typeof item === "string") return item;
|
|
37
|
+
if (typeof item === "object") {
|
|
38
|
+
if (objectKey && item[objectKey] !== undefined) {
|
|
39
|
+
return item[objectKey];
|
|
40
|
+
}
|
|
41
|
+
return JSON.stringify(item);
|
|
42
|
+
}
|
|
43
|
+
return String(item);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const getKey = (item, index) => {
|
|
47
|
+
if (keyExtractor) return keyExtractor(item, index);
|
|
48
|
+
if (typeof item === "object" && item?.id !== undefined) {
|
|
49
|
+
return item.id.toString();
|
|
50
|
+
}
|
|
51
|
+
return index.toString();
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const isSelected = (item) => {
|
|
55
|
+
if (!value) return false;
|
|
56
|
+
|
|
57
|
+
if (typeof item === "string") {
|
|
58
|
+
return value === item;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (typeof item === "object") {
|
|
62
|
+
if (objectKey) {
|
|
63
|
+
return value?.[objectKey] === item?.[objectKey];
|
|
64
|
+
}
|
|
65
|
+
return value === item;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return false;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const openDropdown = () => {
|
|
72
|
+
if (!triggerRef.current) return;
|
|
73
|
+
|
|
74
|
+
triggerRef.current.measure((fx, fy, width, height, px, py) => {
|
|
75
|
+
setLayout({
|
|
76
|
+
x: px,
|
|
77
|
+
y: py,
|
|
78
|
+
width,
|
|
79
|
+
height
|
|
80
|
+
});
|
|
81
|
+
setVisible(true);
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const closeDropdown = () => {
|
|
86
|
+
setVisible(false);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const handleSelect = (item) => {
|
|
90
|
+
onChange(item);
|
|
91
|
+
closeDropdown();
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const dropdownPosition = () => {
|
|
95
|
+
if (!layout) return {};
|
|
96
|
+
|
|
97
|
+
const spaceBelow = screenHeight - (layout.y + layout.height);
|
|
98
|
+
const dropdownHeight = Math.min(options.length * 44, 250);
|
|
99
|
+
|
|
100
|
+
// If not enough space below, open upward
|
|
101
|
+
if (spaceBelow < dropdownHeight) {
|
|
102
|
+
return {
|
|
103
|
+
top: layout.y - dropdownHeight,
|
|
104
|
+
left: layout.x,
|
|
105
|
+
width: layout.width,
|
|
106
|
+
maxHeight: 250
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Default open downward
|
|
111
|
+
return {
|
|
112
|
+
top: layout.y + layout.height,
|
|
113
|
+
left: layout.x,
|
|
114
|
+
width: layout.width,
|
|
115
|
+
maxHeight: 250
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<View style={styles.wrapper}>
|
|
121
|
+
<Text
|
|
122
|
+
color={COLORS.slate[800]}
|
|
123
|
+
style={[{ fontWeight: "600" }, labelStyle]}
|
|
124
|
+
>
|
|
125
|
+
{label}
|
|
126
|
+
</Text>
|
|
127
|
+
|
|
128
|
+
<Pressable
|
|
129
|
+
ref={triggerRef}
|
|
130
|
+
style={[styles.box, style]}
|
|
131
|
+
onPress={openDropdown}
|
|
132
|
+
>
|
|
133
|
+
<Inline gap={10} alignItems="center">
|
|
134
|
+
<View
|
|
135
|
+
style={[
|
|
136
|
+
{
|
|
137
|
+
minHeight: 28,
|
|
138
|
+
flex: 1,
|
|
139
|
+
justifyContent: "center"
|
|
140
|
+
},
|
|
141
|
+
inputStyle
|
|
142
|
+
]}
|
|
143
|
+
>
|
|
144
|
+
<Text>
|
|
145
|
+
{value ? getLabel(value) : "Select..."}
|
|
146
|
+
</Text>
|
|
147
|
+
</View>
|
|
148
|
+
|
|
149
|
+
<MaterialIcons
|
|
150
|
+
name={visible ? "expand-less" : "expand-more"}
|
|
151
|
+
size={20}
|
|
152
|
+
/>
|
|
153
|
+
</Inline>
|
|
154
|
+
</Pressable>
|
|
155
|
+
|
|
156
|
+
<Modal
|
|
157
|
+
transparent
|
|
158
|
+
visible={visible}
|
|
159
|
+
animationType="fade"
|
|
160
|
+
onRequestClose={closeDropdown}
|
|
161
|
+
>
|
|
162
|
+
<Pressable
|
|
163
|
+
style={styles.overlay}
|
|
164
|
+
onPress={closeDropdown}
|
|
165
|
+
>
|
|
166
|
+
{layout && (
|
|
167
|
+
<View
|
|
168
|
+
style={[
|
|
169
|
+
styles.optionArea,
|
|
170
|
+
dropdownPosition()
|
|
171
|
+
]}
|
|
172
|
+
>
|
|
173
|
+
<ScrollView>
|
|
174
|
+
{options.map((item, index) => (
|
|
175
|
+
<TouchableOpacity
|
|
176
|
+
key={index}
|
|
177
|
+
style={[
|
|
178
|
+
styles.optionItem,
|
|
179
|
+
isSelected(item) && styles.selectedItem
|
|
180
|
+
]}
|
|
181
|
+
onPress={() => handleSelect(item)}
|
|
182
|
+
>
|
|
183
|
+
<Text color={isSelected(item) ? COLORS.primary : null} style={{fontWeight: isSelected(item) ? '700' : '400'}}>
|
|
184
|
+
{getLabel(item)}
|
|
185
|
+
</Text>
|
|
186
|
+
</TouchableOpacity>
|
|
187
|
+
))}
|
|
188
|
+
</ScrollView>
|
|
189
|
+
</View>
|
|
190
|
+
)}
|
|
191
|
+
</Pressable>
|
|
192
|
+
</Modal>
|
|
193
|
+
</View>
|
|
194
|
+
);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const styles = StyleSheet.create({
|
|
198
|
+
wrapper: {
|
|
199
|
+
gap: 8,
|
|
200
|
+
},
|
|
201
|
+
box: {
|
|
202
|
+
borderWidth: 0.5,
|
|
203
|
+
borderRadius: 12,
|
|
204
|
+
borderColor: COLORS.slate[300],
|
|
205
|
+
padding: 10
|
|
206
|
+
},
|
|
207
|
+
overlay: {
|
|
208
|
+
flex: 1
|
|
209
|
+
},
|
|
210
|
+
optionArea: {
|
|
211
|
+
position: "absolute",
|
|
212
|
+
backgroundColor: "#fff",
|
|
213
|
+
borderRadius: 12,
|
|
214
|
+
borderWidth: 1,
|
|
215
|
+
borderColor: COLORS.slate[300],
|
|
216
|
+
elevation: 10,
|
|
217
|
+
padding: 10,
|
|
218
|
+
marginTop: 10
|
|
219
|
+
},
|
|
220
|
+
optionItem: {
|
|
221
|
+
padding: 15,
|
|
222
|
+
borderRadius: 8,
|
|
223
|
+
},
|
|
224
|
+
selectedItem: {
|
|
225
|
+
backgroundColor: `${COLORS.primary}15`
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
export default Dropdown;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React, { useEffect, useState, memo } from "react";
|
|
2
|
+
import { StyleSheet, View } from "react-native";
|
|
3
|
+
|
|
4
|
+
const Grid = ({ children, col = 2, gap = 20, style }) => {
|
|
5
|
+
const [containerWidth, setContainerWidth] = useState(0);
|
|
6
|
+
const [itemWidth, setItemWidth] = useState(0);
|
|
7
|
+
const handleLayout = (event) => {
|
|
8
|
+
const newWidth = event.nativeEvent.layout.width;
|
|
9
|
+
setContainerWidth(prev => (prev !== newWidth ? newWidth : prev));
|
|
10
|
+
};
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (containerWidth > 0 && col > 0) {
|
|
13
|
+
const totalGap = gap * (col - 1);
|
|
14
|
+
const calculatedWidth = (containerWidth - totalGap) / col;
|
|
15
|
+
setItemWidth(prev => prev !== calculatedWidth ? calculatedWidth : prev);
|
|
16
|
+
}
|
|
17
|
+
}, [containerWidth, col, gap]);
|
|
18
|
+
return (React.createElement(View, { style: [styles.container, { gap }, style], onLayout: handleLayout }, React.Children.map(children, (child, index) => (React.createElement(View, { key: index, style: { width: itemWidth } }, child)))));
|
|
19
|
+
};
|
|
20
|
+
const styles = StyleSheet.create({
|
|
21
|
+
container: {
|
|
22
|
+
flexDirection: "row",
|
|
23
|
+
flexWrap: "wrap"
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
export default memo(Grid);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { StyleSheet, TouchableOpacity, View } from "react-native";
|
|
3
|
+
|
|
4
|
+
const Inline = ({children, gap = 20, alignItems = 'center', justifyContent, onPress = null, onLongPress = null, style}) => {
|
|
5
|
+
let computedStyles = [
|
|
6
|
+
styles.container,
|
|
7
|
+
{
|
|
8
|
+
gap,
|
|
9
|
+
alignItems,
|
|
10
|
+
justifyContent
|
|
11
|
+
},
|
|
12
|
+
style
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
if (onPress === null && onLongPress === null) {
|
|
16
|
+
return (
|
|
17
|
+
<View style={computedStyles}>
|
|
18
|
+
{children}
|
|
19
|
+
</View>
|
|
20
|
+
)
|
|
21
|
+
} else {
|
|
22
|
+
return (
|
|
23
|
+
<TouchableOpacity onPress={onPress} onLongPress={onLongPress} style={computedStyles}>
|
|
24
|
+
{children}
|
|
25
|
+
</TouchableOpacity>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const styles = StyleSheet.create({
|
|
31
|
+
container: {
|
|
32
|
+
flexDirection: 'row',
|
|
33
|
+
// flexWrap: 'wrap'
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
export default Inline;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
|
+
import Inline from "./Inline";
|
|
3
|
+
import { Pressable, StyleSheet, TextInput, View } from "react-native";
|
|
4
|
+
import Text from "./Text";
|
|
5
|
+
import COLORS from "./COLORS";
|
|
6
|
+
|
|
7
|
+
const Input = ({label = 'Nama', labelStyle, style, inputStyle, value, onChangeText, onChange, keyboardType = "default", onFocus, onBlur, left = null, right = null, ...props}) => {
|
|
8
|
+
const [isFocused, setFocused] = useState(false);
|
|
9
|
+
const inputRef = useRef(null);
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<Pressable style={[
|
|
13
|
+
styles.container
|
|
14
|
+
]}>
|
|
15
|
+
<Text color={COLORS.slate[800]} style={[
|
|
16
|
+
{
|
|
17
|
+
fontWeight: '600',
|
|
18
|
+
},
|
|
19
|
+
labelStyle
|
|
20
|
+
]}>{label}</Text>
|
|
21
|
+
<Inline style={[
|
|
22
|
+
styles.box,
|
|
23
|
+
isFocused ? {borderColor: '#ffffff00'} : null,
|
|
24
|
+
style,
|
|
25
|
+
]} gap={10}>
|
|
26
|
+
{
|
|
27
|
+
isFocused &&
|
|
28
|
+
<Pressable style={styles.input_active} />
|
|
29
|
+
}
|
|
30
|
+
{left}
|
|
31
|
+
<TextInput
|
|
32
|
+
ref={inputRef}
|
|
33
|
+
style={[
|
|
34
|
+
{
|
|
35
|
+
height: props?.multiline ? 'auto' : 28,
|
|
36
|
+
minHeight: 28,
|
|
37
|
+
flexGrow: 1,
|
|
38
|
+
flexShrink: 1,
|
|
39
|
+
},
|
|
40
|
+
inputStyle
|
|
41
|
+
]}
|
|
42
|
+
onFocus={() => {
|
|
43
|
+
setFocused(true);
|
|
44
|
+
if (onFocus) {
|
|
45
|
+
onFocus();
|
|
46
|
+
}
|
|
47
|
+
}}
|
|
48
|
+
onBlur={() => {
|
|
49
|
+
setFocused(false);
|
|
50
|
+
if (onBlur) {
|
|
51
|
+
onBlur();
|
|
52
|
+
}
|
|
53
|
+
}}
|
|
54
|
+
value={value}
|
|
55
|
+
onChangeText={e => {
|
|
56
|
+
onChangeText(e);
|
|
57
|
+
}}
|
|
58
|
+
onChange={onChange}
|
|
59
|
+
keyboardType={keyboardType}
|
|
60
|
+
{...props}
|
|
61
|
+
/>
|
|
62
|
+
{right}
|
|
63
|
+
</Inline>
|
|
64
|
+
</Pressable>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const styles = StyleSheet.create({
|
|
69
|
+
container: {
|
|
70
|
+
position: 'relative',
|
|
71
|
+
gap: 8,
|
|
72
|
+
flexGrow: 1,
|
|
73
|
+
},
|
|
74
|
+
box: {
|
|
75
|
+
borderWidth: 0.5,
|
|
76
|
+
borderRadius: 12,
|
|
77
|
+
borderColor: COLORS.slate[300],
|
|
78
|
+
padding: 10,
|
|
79
|
+
},
|
|
80
|
+
input_active: {
|
|
81
|
+
borderWidth: 4,
|
|
82
|
+
borderColor: `${COLORS.primary}40`,
|
|
83
|
+
position: 'absolute',
|
|
84
|
+
top: -3,left: -3,right: -3,bottom: -3,
|
|
85
|
+
borderRadius: 12,
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
export default Input;
|