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.
Files changed (70) hide show
  1. package/README.md +3 -0
  2. package/components/Alert.jsx +74 -0
  3. package/components/Avatar.jsx +144 -0
  4. package/components/BottomSheet.jsx +187 -0
  5. package/components/Breadcrumb.jsx +48 -0
  6. package/components/Button.jsx +58 -0
  7. package/components/COLORS.js +138 -0
  8. package/components/Card.jsx +33 -0
  9. package/components/Checkbox.jsx +39 -0
  10. package/components/Dialog.jsx +188 -0
  11. package/components/Divider.jsx +62 -0
  12. package/components/Dropdown.jsx +229 -0
  13. package/components/Grid.jsx +26 -0
  14. package/components/Inline.jsx +37 -0
  15. package/components/Input.jsx +89 -0
  16. package/components/ProgressBar.jsx +65 -0
  17. package/components/Rate.jsx +39 -0
  18. package/components/Slider.jsx +219 -0
  19. package/components/Switch.jsx +45 -0
  20. package/components/Table.jsx +67 -0
  21. package/components/Text.jsx +56 -0
  22. package/components/Toggle.jsx +88 -0
  23. package/index.js +21 -2
  24. package/package.json +19 -23
  25. package/babel.config.js +0 -3
  26. package/components/BottomSheet.tsx +0 -197
  27. package/components/Carousel.tsx +0 -61
  28. package/components/Circle.tsx +0 -44
  29. package/components/DatePicker.jsx +0 -181
  30. package/components/Tab.tsx +0 -90
  31. package/components/Table.tsx +0 -95
  32. package/components/index.ts +0 -5
  33. package/config.js +0 -4
  34. package/dist/BottomSheet.js +0 -186
  35. package/dist/Button.js +0 -109
  36. package/dist/Carousel.js +0 -52
  37. package/dist/Circle.js +0 -42
  38. package/dist/DatePicker.js +0 -199
  39. package/dist/Dialog.js +0 -81
  40. package/dist/Dropdown.js +0 -97
  41. package/dist/Inline.js +0 -38
  42. package/dist/Input.js +0 -88
  43. package/dist/ProgressBar.js +0 -64
  44. package/dist/Separator.js +0 -47
  45. package/dist/Skeleton.js +0 -62
  46. package/dist/Switch.js +0 -74
  47. package/dist/Tab.js +0 -85
  48. package/dist/Table.js +0 -96
  49. package/dist/Text.js +0 -78
  50. package/dist/Toast.js +0 -72
  51. package/dist/Toggle.js +0 -54
  52. package/dist/index.js +0 -96
  53. package/elements/Button.tsx +0 -121
  54. package/elements/ColorPicker.tsx +0 -70
  55. package/elements/Dialog.tsx +0 -87
  56. package/elements/Dropdown.tsx +0 -88
  57. package/elements/Inline.tsx +0 -52
  58. package/elements/Input.tsx +0 -83
  59. package/elements/ProgressBar.tsx +0 -52
  60. package/elements/SecureStorage.js +0 -27
  61. package/elements/Separator.tsx +0 -71
  62. package/elements/Skeleton.tsx +0 -64
  63. package/elements/Slider.tsx +0 -133
  64. package/elements/Switch.tsx +0 -63
  65. package/elements/Text.tsx +0 -73
  66. package/elements/Toast.tsx +0 -71
  67. package/elements/Toggle.tsx +0 -59
  68. package/elements/index.js +0 -14
  69. package/index.d.ts +0 -237
  70. 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;