ripal-ui 1.1.394 → 2.0.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.
Files changed (65) hide show
  1. package/README.md +3 -0
  2. package/components/Alert.jsx +74 -0
  3. package/components/Avatar.jsx +157 -0
  4. package/components/Button.jsx +58 -0
  5. package/components/COLORS.js +138 -0
  6. package/components/Checkbox.jsx +51 -0
  7. package/components/Dialog.jsx +221 -0
  8. package/components/Divider.jsx +62 -0
  9. package/components/Dropdown.jsx +229 -0
  10. package/components/Inline.jsx +37 -0
  11. package/components/Input.jsx +89 -0
  12. package/components/Rate.jsx +39 -0
  13. package/components/Slider.jsx +219 -0
  14. package/components/Switch.jsx +45 -0
  15. package/components/Table.jsx +67 -0
  16. package/components/Text.jsx +56 -0
  17. package/components/Toggle.jsx +88 -0
  18. package/index.js +16 -2
  19. package/package.json +20 -20
  20. package/babel.config.js +0 -3
  21. package/components/BottomSheet.tsx +0 -197
  22. package/components/Carousel.tsx +0 -61
  23. package/components/Circle.tsx +0 -44
  24. package/components/DatePicker.jsx +0 -181
  25. package/components/Tab.tsx +0 -90
  26. package/components/Table.tsx +0 -95
  27. package/components/index.ts +0 -5
  28. package/config.js +0 -4
  29. package/dist/BottomSheet.js +0 -186
  30. package/dist/Button.js +0 -109
  31. package/dist/Carousel.js +0 -52
  32. package/dist/Circle.js +0 -42
  33. package/dist/DatePicker.js +0 -199
  34. package/dist/Dialog.js +0 -81
  35. package/dist/Dropdown.js +0 -97
  36. package/dist/Inline.js +0 -38
  37. package/dist/Input.js +0 -88
  38. package/dist/ProgressBar.js +0 -64
  39. package/dist/Separator.js +0 -47
  40. package/dist/Skeleton.js +0 -62
  41. package/dist/Switch.js +0 -74
  42. package/dist/Tab.js +0 -85
  43. package/dist/Table.js +0 -96
  44. package/dist/Text.js +0 -78
  45. package/dist/Toast.js +0 -72
  46. package/dist/Toggle.js +0 -54
  47. package/dist/index.js +0 -96
  48. package/elements/Button.tsx +0 -121
  49. package/elements/ColorPicker.tsx +0 -70
  50. package/elements/Dialog.tsx +0 -87
  51. package/elements/Dropdown.tsx +0 -88
  52. package/elements/Inline.tsx +0 -52
  53. package/elements/Input.tsx +0 -83
  54. package/elements/ProgressBar.tsx +0 -52
  55. package/elements/SecureStorage.js +0 -27
  56. package/elements/Separator.tsx +0 -71
  57. package/elements/Skeleton.tsx +0 -64
  58. package/elements/Slider.tsx +0 -133
  59. package/elements/Switch.tsx +0 -63
  60. package/elements/Text.tsx +0 -95
  61. package/elements/Toast.tsx +0 -71
  62. package/elements/Toggle.tsx +0 -59
  63. package/elements/index.js +0 -14
  64. package/index.d.ts +0 -237
  65. package/scripts/generateConfig.js +0 -80
@@ -0,0 +1,221 @@
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
+ * @typedef {Object} DialogProps
10
+ * @property {boolean} [visible] - State to control dialog visibility (useState)
11
+ * @property {void} [setVisible] - Function to mutate the visible state (useState)
12
+ * @property {void} [onClose] - Close event
13
+ */
14
+
15
+ /**
16
+ * @typedef {Object} BodyProps
17
+ * @property {int|null} [gap] - Flex gap inside the dialog's body
18
+ */
19
+
20
+ /**
21
+ * @typedef {Object} TitleProps
22
+ * @property {bool|null} [withClose] - Set to false to hide close button
23
+ */
24
+
25
+ /**
26
+ * @typedef {Object} ActionProps
27
+ * @property {int|null} [gap] - Flex gap inside the dialog's body
28
+ */
29
+
30
+ const DialogContext = createContext(null);
31
+
32
+ const useDialog = () => {
33
+ const ctx = useContext(DialogContext);
34
+ if (!ctx) throw new Error("Dialog subcomponents must be used inside Dialog");
35
+ return ctx;
36
+ };
37
+
38
+ /**
39
+ * @param {DialogProps} props
40
+ */
41
+ const Dialog = ({
42
+ children,
43
+ visible = false,
44
+ onClose,
45
+ setVisible = null,
46
+ keyboardOffset = 0
47
+ }) => {
48
+
49
+ const [isVisible, setIsVisible] = useState(visible);
50
+
51
+ useEffect(() => {
52
+ setIsVisible(visible);
53
+ }, [visible]);
54
+
55
+ const closeHandler = () => {
56
+ const next = false;
57
+
58
+ if (onClose) onClose();
59
+ if (setVisible) setVisible(next);
60
+
61
+ setIsVisible(next);
62
+ };
63
+
64
+ return (
65
+ <DialogContext.Provider
66
+ value={{
67
+ visible: isVisible,
68
+ close: closeHandler,
69
+ setVisible
70
+ }}
71
+ >
72
+ <Modal
73
+ transparent
74
+ visible={isVisible}
75
+ onRequestClose={closeHandler}
76
+ statusBarTranslucent
77
+ animationType="fade"
78
+ >
79
+ <KeyboardAvoidingView
80
+ style={styles.wrapper}
81
+ behavior={Platform.OS === "ios" ? "padding" : "height"}
82
+ keyboardVerticalOffset={keyboardOffset}
83
+ >
84
+
85
+ {/* tap outside dialog */}
86
+ <Pressable
87
+ style={StyleSheet.absoluteFill}
88
+ onPress={closeHandler}
89
+ />
90
+
91
+ {/* dismiss keyboard when tap inside empty area */}
92
+ <TouchableWithoutFeedback onPress={() => {
93
+ Keyboard.dismiss();
94
+ closeHandler();
95
+ }}>
96
+ <View style={styles.center}>
97
+ <Pressable
98
+ style={styles.container}
99
+ onPress={(e) => e.stopPropagation()}
100
+ >
101
+ {children}
102
+ </Pressable>
103
+ </View>
104
+ </TouchableWithoutFeedback>
105
+
106
+ </KeyboardAvoidingView>
107
+ </Modal>
108
+ </DialogContext.Provider>
109
+ );
110
+ };
111
+
112
+ /**
113
+ * @param {BodyProps} props
114
+ */
115
+ Dialog.Body = ({ children, gap = 20 }) => {
116
+ const { close } = useDialog();
117
+
118
+ return (
119
+ <View style={{padding: 20, gap: gap }}>
120
+ {children}
121
+ </View>
122
+ )
123
+ }
124
+
125
+ /**
126
+ * @param {TitleProps} props
127
+ */
128
+ Dialog.Title = ({ children, withClose = true }) => {
129
+ const { close } = useDialog();
130
+
131
+ return (
132
+ <Inline style={{padding: 20}}>
133
+ {
134
+ typeof children === "string"
135
+ ? (
136
+ <Text
137
+ size={20}
138
+ color={COLORS.slate[800]}
139
+ style={styles.title_text}
140
+ >
141
+ {children}
142
+ </Text>
143
+ )
144
+ : (
145
+ <View style={{ gap: 5, flexGrow: 1 }}>
146
+ {children}
147
+ </View>
148
+ )
149
+ }
150
+
151
+ {withClose && (
152
+ <Button
153
+ circle
154
+ color={`${COLORS.slate[200]}aa`}
155
+ onPress={close}
156
+ >
157
+ <MaterialIcons name="close" />
158
+ </Button>
159
+ )}
160
+ </Inline>
161
+ );
162
+ };
163
+
164
+ /**
165
+ * @param {ActionProps} props
166
+ */
167
+ Dialog.Actions = ({ children, gap = 10 }) => {
168
+ return (
169
+ <Inline style={[styles.action_area]} gap={gap}>
170
+ {children}
171
+ </Inline>
172
+ );
173
+ };
174
+
175
+ const styles = StyleSheet.create({
176
+ wrapperss: {
177
+ alignItems: 'center',
178
+ justifyContent: 'center',
179
+ flexGrow: 1,
180
+ position: 'absolute',
181
+ top: 0,left: 0,right: 0,bottom: 0,
182
+ backgroundColor: '#00000080',
183
+ padding: 20,
184
+ },
185
+ wrapper: {
186
+ flex: 1,
187
+ backgroundColor: '#00000080',
188
+ },
189
+ center: {
190
+ flex: 1,
191
+ justifyContent: "center",
192
+ alignItems: "center",
193
+ padding: 20,
194
+ },
195
+ container: {
196
+ backgroundColor: "#fff",
197
+ borderRadius: 14,
198
+ width: "100%",
199
+ maxWidth: 500,
200
+ },
201
+ containerss: {
202
+ backgroundColor: '#fff',
203
+ padding: 0,
204
+ flexGrow: 1,
205
+ borderRadius: 14,
206
+ },
207
+ title_text: {
208
+ fontWeight: '700',
209
+ flexGrow: 1,
210
+ },
211
+ action_area: {
212
+ padding: 20,
213
+ justifyContent: 'flex-end',
214
+ backgroundColor: COLORS.slate[100],
215
+ borderBottomLeftRadius: 14,
216
+ borderBottomRightRadius: 14,
217
+ marginTop: 20,
218
+ }
219
+ })
220
+
221
+ 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,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;
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+ import { StyleSheet, TouchableOpacity } from "react-native";
3
+ import Inline from "./Inline";
4
+ import MaterialIcons from "@react-native-vector-icons/material-icons";
5
+ import COLORS from "./COLORS";
6
+
7
+ const Rate = ({rating = 4, setRating = null, icon = null, gap = 5}) => {
8
+ return (
9
+ <Inline gap={gap}>
10
+ {
11
+ [1,1,1,1,1].map((item, i) => {
12
+ let color = i + 1 <= rating ? COLORS.yellow[500] : COLORS.slate[300];
13
+
14
+ if (icon !== null) {
15
+ return icon({
16
+ color
17
+ })
18
+ }
19
+
20
+ return (
21
+ <TouchableOpacity key={i} onPress={() => {
22
+ if (setRating !== null) {
23
+ setRating(i + 1);
24
+ }
25
+ }}>
26
+ <MaterialIcons name="star" size={24} color={color} />
27
+ </TouchableOpacity>
28
+ )
29
+ })
30
+ }
31
+ </Inline>
32
+ )
33
+ }
34
+
35
+ const styles = StyleSheet.create({
36
+ //
37
+ })
38
+
39
+ export default Rate;