ripal-ui 1.0.0 → 1.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.
@@ -0,0 +1,52 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import { View, StyleSheet, ViewStyle } from "react-native";
3
+ import config from "../config";
4
+
5
+ interface ProgressBarProps {
6
+ value: number;
7
+ from?: number;
8
+ size?: number;
9
+ style?: ViewStyle;
10
+ percentage?: number | null;
11
+ }
12
+
13
+ const ProgressBar: React.FC<ProgressBarProps> = ({
14
+ value,
15
+ from = 100,
16
+ size = 5,
17
+ style,
18
+ percentage = null,
19
+ }) => {
20
+ const [percent, setPercent] = useState<number | null>(null);
21
+
22
+ useEffect(() => {
23
+ if (percent == null) {
24
+ setPercent((value / from) * 100);
25
+ }
26
+ }, [value, from, percent]); // Add dependencies to useEffect
27
+
28
+ return (
29
+ <View style={{ ...barStyle.area, ...style, height: size }}>
30
+ <View
31
+ style={{
32
+ ...barStyle.value,
33
+ width: `${percent !== null ? percent : 0}%`,
34
+ height: size,
35
+ }}
36
+ ></View>
37
+ </View>
38
+ );
39
+ };
40
+
41
+ const barStyle = StyleSheet.create({
42
+ area: {
43
+ backgroundColor: config.colors.slate[200],
44
+ height: 5,
45
+ flexGrow: 1,
46
+ },
47
+ value: {
48
+ backgroundColor: config.colors.primary,
49
+ },
50
+ });
51
+
52
+ export default ProgressBar;
@@ -0,0 +1,72 @@
1
+ import React from "react";
2
+ import { Dimensions, StyleSheet, View, ViewStyle } from "react-native";
3
+ import Inline from "./Inline";
4
+ import config from "../config";
5
+ import Text from "./Text";
6
+
7
+ interface SeparatorProps {
8
+ width?: any;
9
+ space?: number;
10
+ height?: number;
11
+ color?: string;
12
+ textProps?: React.ComponentProps<typeof Text>;
13
+ style?: ViewStyle;
14
+ children?: React.ReactNode;
15
+ }
16
+
17
+ const Separator: React.FC<SeparatorProps> = ({
18
+ children,
19
+ width = "100%",
20
+ space = 20,
21
+ height = 1,
22
+ color = config.colors.slate[200],
23
+ textProps,
24
+ style,
25
+ }) => {
26
+ let theWidth = width;
27
+ if (typeof width === "string") {
28
+ let toReturn = [];
29
+ let w = width.split("");
30
+ w.map((wi, w) => {
31
+ if (!isNaN(wi)) {
32
+ toReturn.push(wi);
33
+ }
34
+ });
35
+ toReturn = parseInt(toReturn.join(""));
36
+ theWidth = toReturn / 100 * Dimensions.get('screen').width;
37
+ console.log(theWidth);
38
+ }
39
+ return (
40
+ <Inline justifyContent="center">
41
+ <View
42
+ style={{
43
+ backgroundColor: color,
44
+ width: theWidth,
45
+ height: height,
46
+ marginVertical: space,
47
+ ...style,
48
+ }}
49
+ ></View>
50
+ {children && (
51
+ <Text
52
+ size={12}
53
+ style={{
54
+ position: "absolute",
55
+ backgroundColor: "#fff",
56
+ padding: 10,
57
+ paddingHorizontal: 20,
58
+ }}
59
+ {...textProps}
60
+ >
61
+ {children}
62
+ </Text>
63
+ )}
64
+ </Inline>
65
+ );
66
+ };
67
+
68
+ const styles = StyleSheet.create({
69
+ // Define your styles here if needed
70
+ });
71
+
72
+ export default Separator;
@@ -0,0 +1,64 @@
1
+ import React, { useEffect, useRef } from "react";
2
+ import { Animated, StyleSheet, View, ViewStyle } from "react-native";
3
+ import config from "../config";
4
+
5
+ interface SkeletonProps {
6
+ color?: string;
7
+ rounded?: number;
8
+ width?: any;
9
+ height?: number;
10
+ aspectRatio?: number | null;
11
+ }
12
+
13
+ const Skeleton: React.FC<SkeletonProps> = ({
14
+ color = config.colors.slate[200],
15
+ rounded = 8,
16
+ width = `${Math.floor(Math.random() * (99 - 11 + 1)) + 11}%`,
17
+ height = 20,
18
+ aspectRatio = null,
19
+ }) => {
20
+ const aspectRatioStyles: ViewStyle = { aspectRatio: aspectRatio ?? 1 };
21
+ const heightStyles: ViewStyle = { height: height };
22
+ const opacity = useRef(new Animated.Value(1)).current;
23
+ const defaultStyles: ViewStyle = {
24
+ backgroundColor: color,
25
+ width,
26
+ borderRadius: rounded,
27
+ opacity,
28
+ };
29
+
30
+ useEffect(() => {
31
+ const bounce = () => {
32
+ opacity.setValue(1);
33
+ Animated.timing(opacity, {
34
+ toValue: 0.4,
35
+ duration: 1000,
36
+ useNativeDriver: true,
37
+ }).start(() => {
38
+ Animated.timing(opacity, {
39
+ toValue: 0.8,
40
+ duration: 1000,
41
+ useNativeDriver: true,
42
+ }).start(bounce);
43
+ });
44
+ };
45
+
46
+ bounce();
47
+ }, [opacity]);
48
+
49
+ return (
50
+ <Animated.View
51
+ style={
52
+ aspectRatio === null
53
+ ? [defaultStyles, heightStyles]
54
+ : [defaultStyles, aspectRatioStyles]
55
+ }
56
+ />
57
+ );
58
+ };
59
+
60
+ const styles = StyleSheet.create({
61
+ // Add your styles here if needed
62
+ });
63
+
64
+ export default Skeleton;
@@ -0,0 +1,64 @@
1
+ import React, { useState } from "react";
2
+ import { Pressable, StyleSheet, View, ViewStyle } from "react-native";
3
+ import config from "../config";
4
+ import Inline from "./Inline";
5
+
6
+ interface SwitchProps {
7
+ active?: boolean;
8
+ size?: number;
9
+ spacer?: number;
10
+ onChange?: () => void;
11
+ }
12
+
13
+ const Switch: React.FC<SwitchProps> = ({
14
+ active = false,
15
+ size = 24,
16
+ spacer = 5,
17
+ onChange = null,
18
+ }) => {
19
+ const [isActive, setActive] = useState(active);
20
+
21
+ return (
22
+ <Pressable
23
+ onPress={() => {
24
+ setActive(!isActive);
25
+ if (onChange) {
26
+ onChange();
27
+ }
28
+ console.log('hehe');
29
+ }}
30
+ >
31
+ <Inline
32
+ style={{
33
+ backgroundColor: isActive ? config.colors.green[500] : config.colors.slate[200],
34
+ padding: spacer,
35
+ borderRadius: 999,
36
+ width: size * 2 + spacer + 5,
37
+ }}
38
+ >
39
+ {isActive && <View style={{ flexGrow: 1 }}></View>}
40
+ <View
41
+ style={{
42
+ height: size,
43
+ ...styles.circle,
44
+ }}
45
+ >
46
+ {/* Add any inner content here if needed */}
47
+ </View>
48
+ </Inline>
49
+ </Pressable>
50
+ );
51
+ };
52
+
53
+ const styles = StyleSheet.create({
54
+ area: {
55
+ backgroundColor: config.colors.slate[100],
56
+ },
57
+ circle: {
58
+ backgroundColor: '#fff',
59
+ aspectRatio: 1,
60
+ borderRadius: 9999,
61
+ },
62
+ });
63
+
64
+ export default Switch;
@@ -0,0 +1,73 @@
1
+ import React, { FC } from "react";
2
+ import { Text as RNText, TextStyle } from "react-native";
3
+ import { useFonts, Poppins_300Light, Poppins_400Regular, Poppins_500Medium, Poppins_600SemiBold, Poppins_700Bold, Poppins_900Black } from "@expo-google-fonts/poppins";
4
+ import config from "../config";
5
+
6
+ interface TextProps {
7
+ children: React.ReactNode;
8
+ weight?: '300Light' | '400Regular' | '500Medium' | '600SemiBold' | '700Bold' | '900Black';
9
+ size?: number;
10
+ color?: string;
11
+ align?: 'left' | 'right' | 'center' | 'justify';
12
+ limit?: number;
13
+ spacing?: number;
14
+ lineHeight?: number | null;
15
+ style?: TextStyle;
16
+ }
17
+
18
+ const Text: FC<TextProps> = ({
19
+ children,
20
+ weight = "400Regular",
21
+ size,
22
+ color = config.colors.slate[500],
23
+ align = "left",
24
+ limit = 0,
25
+ spacing = 0,
26
+ lineHeight = null,
27
+ style
28
+ }) => {
29
+ const fontName = "Poppins";
30
+ const [fontsLoaded] = useFonts({
31
+ Poppins_300Light,
32
+ Poppins_400Regular,
33
+ Poppins_500Medium,
34
+ Poppins_600SemiBold,
35
+ Poppins_700Bold,
36
+ Poppins_900Black
37
+ });
38
+
39
+ if (config.appLangs.default !== null) {
40
+ const ogChildren = children;
41
+ if (typeof children === "string") {
42
+ children = children.split('.').reduce((acc, key) => {
43
+ return acc && acc[key] !== undefined ? acc[key] : ogChildren;
44
+ }, config.appLangs[config.appLangs.default]);
45
+ }
46
+ }
47
+
48
+ if (!fontsLoaded) {
49
+ return <RNText>{children}</RNText>; // Return statement added to properly handle loading state
50
+ }
51
+
52
+ if (limit > 0 && typeof children === "string" && children.length > limit) {
53
+ children = children.substr(0, limit) + '...';
54
+ }
55
+
56
+ return (
57
+ <RNText
58
+ style={{
59
+ fontSize: size,
60
+ color: color,
61
+ letterSpacing: spacing,
62
+ lineHeight: lineHeight,
63
+ ...style,
64
+ fontFamily: `${fontName}_${weight}`,
65
+ textAlign: align
66
+ }}
67
+ >
68
+ {children}
69
+ </RNText>
70
+ );
71
+ };
72
+
73
+ export default Text;
@@ -0,0 +1,65 @@
1
+ import React, { useEffect } from "react";
2
+ import { StyleSheet, View, ViewStyle } from "react-native";
3
+ import Inline from "./Inline";
4
+ import Text from "./Text";
5
+ import config from "../config";
6
+
7
+ interface ToastProps {
8
+ label?: string;
9
+ right?: React.ReactNode; // Allows for any valid React node
10
+ visible?: boolean;
11
+ setVisible: (visible: boolean) => void;
12
+ timeout?: number;
13
+ containerStyle?: ViewStyle; // Style for the container View
14
+ style?: ViewStyle; // Additional style for the Toast
15
+ textProps?: React.ComponentProps<typeof Text>; // Props for the Text component
16
+ }
17
+
18
+ const Toast: React.FC<ToastProps> = ({
19
+ label = "Percakapan berhasil dihapus",
20
+ right = null,
21
+ visible = true,
22
+ setVisible,
23
+ timeout = 3000,
24
+ containerStyle,
25
+ textProps,
26
+ }) => {
27
+ useEffect(() => {
28
+ if (visible) {
29
+ const timer = setTimeout(() => {
30
+ setVisible(false);
31
+ }, timeout);
32
+
33
+ return () => clearTimeout(timer);
34
+ }
35
+ }, [visible, setVisible, timeout]);
36
+
37
+ return visible ? (
38
+ <View style={{ ...styles.container, ...containerStyle }}>
39
+ <Inline style={styles.area}>
40
+ <Text size={12} style={{ flexGrow: 1 }} color="#fff" {...textProps}>
41
+ {label}
42
+ </Text>
43
+ {right}
44
+ </Inline>
45
+ </View>
46
+ ) : null;
47
+ };
48
+
49
+ const styles = StyleSheet.create({
50
+ container: {
51
+ position: 'absolute',
52
+ bottom: 0,
53
+ left: 0,
54
+ right: 0,
55
+ padding: 20,
56
+ },
57
+ area: {
58
+ backgroundColor: config.colors.slate[800],
59
+ borderRadius: 999,
60
+ padding: 20,
61
+ paddingHorizontal: 30,
62
+ },
63
+ });
64
+
65
+ export default Toast;
@@ -0,0 +1,59 @@
1
+ import React from "react";
2
+ import { Pressable, StyleSheet, View, ViewStyle } from "react-native";
3
+ import config from "../config";
4
+ import Text from "./Text";
5
+ import Inline from "./Inline";
6
+
7
+ interface ToggleProps {
8
+ options: string[];
9
+ value: any;
10
+ setValue: (value: string) => void;
11
+ }
12
+
13
+ const Toggle: React.FC<ToggleProps> = ({ options, value, setValue }) => {
14
+ return (
15
+ <Inline style={styles.area}>
16
+ {options.map((opt, o) => {
17
+ const isActive = value === opt;
18
+ return (
19
+ <Pressable
20
+ key={o}
21
+ style={[styles.item, isActive ? styles.item_active : null]}
22
+ onPress={() => setValue(opt)}
23
+ >
24
+ <Text
25
+ color={isActive ? config.colors.primary : config.colors.slate[500]}
26
+ weight={isActive ? '600SemiBold' : '400Regular'}
27
+ >
28
+ {opt}
29
+ </Text>
30
+ </Pressable>
31
+ );
32
+ })}
33
+ </Inline>
34
+ );
35
+ };
36
+
37
+ const styles = StyleSheet.create({
38
+ area: {
39
+ padding: 10,
40
+ backgroundColor: config.colors.slate[100],
41
+ borderRadius: 12,
42
+ },
43
+ item: {
44
+ flexGrow: 1,
45
+ flexDirection: 'row',
46
+ alignItems: 'center',
47
+ justifyContent: 'center',
48
+ paddingVertical: 10,
49
+ borderRadius: 12,
50
+ },
51
+ item_active: {
52
+ backgroundColor: '#fff',
53
+ shadowColor: config.colors.slate[300],
54
+ shadowOpacity: 0.4,
55
+ shadowRadius: 12,
56
+ },
57
+ });
58
+
59
+ export default Toggle;
package/elements/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { default as Button } from './Button';
2
- export { default as Dialog } from './Dialog';
2
+ export { Dialog, DialogActions } from './Dialog';
3
3
  export { default as Dropdown } from './Dropdown';
4
4
  export { default as Inline } from './Inline';
5
5
  export { default as Input } from './Input';
@@ -8,4 +8,5 @@ export { default as Separator } from './Separator';
8
8
  export { default as Skeleton } from './Skeleton';
9
9
  export { default as Switch } from './Switch';
10
10
  export { default as Text } from './Text';
11
+ export { default as Toast } from './Toast';
11
12
  export { default as Toggle } from './Toggle';
package/package.json CHANGED
@@ -1,19 +1,23 @@
1
1
  {
2
2
  "name": "ripal-ui",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "A collection of React elements and components",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "build": "babel elements components --out-dir dist"
7
+ "build": "babel elements components --out-dir dist",
8
+ "postinstall": "node ./scripts/generateConfig.js"
8
9
  },
9
10
  "dependencies": {
10
11
  "react": "^18.0.0",
11
- "react-native": "^0.70.0"
12
+ "react-native": "^0.74.2"
12
13
  },
13
14
  "devDependencies": {
14
15
  "@babel/cli": "^7.25.6",
15
16
  "@babel/core": "^7.25.2",
16
17
  "@babel/preset-env": "^7.25.4",
17
- "@babel/preset-react": "^7.24.7"
18
+ "@babel/preset-react": "^7.24.7",
19
+ "@types/react": "^18.3.10",
20
+ "@types/react-native": "^0.73.0",
21
+ "typescript": "^5.6.2"
18
22
  }
19
23
  }
@@ -0,0 +1,79 @@
1
+ // ./scripts/generateConfig.js
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ // Define the config file content
6
+ const configContent = `const config = {
7
+ appName: "",
8
+ appLangs: {
9
+ default: null,
10
+ en: require('./lang/en.json')
11
+ },
12
+ colors: {
13
+ primary: "#3B82F6",
14
+ transparent: "#ffffff00",
15
+ white: "#fff",
16
+ slate: {
17
+ 100: "#F1F5F9",200: "#E2E8F0",300: "#CBD5E1",
18
+ 400: "#94A3B8",500: "#64748B",600: "#475569",
19
+ 700: "#334155",800: "#1E293B",900: "#0F172A",
20
+ },
21
+ green: {
22
+ 100: "#DCFCE7",200: "#BBF7D0",300: "#86EFAC ",
23
+ 400: "#4ADE7F",500: "#22C55E",600: "#16A34A",
24
+ 700: "#15803D",800: "#166534",900: "#14532D",
25
+ },
26
+ orange: {
27
+ 100: "#FFEDD5",200: "#FED7AA",300: "#FDBA74",
28
+ 400: "#FB923C",500: "#F87316",600: "#EA580C",
29
+ 700: "#C2410C",800: "#9A3412",900: "#7C2D12",
30
+ },
31
+ purple: {
32
+ 100: "#F3E8FF",200: "#E9D5FF",300: "#D8B4FE",
33
+ 400: "#C084FC",500: "#A855F7",600: "#9333EA",
34
+ 700: "#7E22CE",800: "#6B21A8",900: "#581C87",
35
+ },
36
+ blue: {
37
+ 100: "#D8EAFE",200: "#BFD8FE",300: "#94C5FD",
38
+ 400: "#60A5FA",500: "#3B82F6",600: "#2563EB",
39
+ 700: "#1D4ED8",800: "#1E41AF",900: "#1E3A8A",
40
+ },
41
+ red: {
42
+ 100: "#FEE2E2",200: "#FECACA",300: "#FCA5A5",
43
+ 400: "#F87171",500: "#EF4444",600: "#DC2626",
44
+ 700: "#B91C1C",800: "#991B1B",900: "#7F1D1D",
45
+ },
46
+ lime: {
47
+ 100: "#ECFCCB",200: "#D9F99D",300: "#BEF264",
48
+ 400: "#A3E635",500: "#84CC16",600: "#65A30D",
49
+ 700: "#4D7C0F",800: "#3F6212",900: "#365314",
50
+ },
51
+ yellow: {
52
+ 100: "#FEF9C3",200: "#FEF08A",300: "#FDE047",
53
+ 400: "#FACC15",500: "#EAB308",600: "#CA8A04",
54
+ 700: "#A16207",800: "#854D0E",900: "#713F12",
55
+ }
56
+ }
57
+ }
58
+
59
+ export default config
60
+ `;
61
+
62
+ // Path to generate the config file in the user's project root
63
+ const configFilePath = path.resolve(process.cwd(), '../../ripal-ui.config.js');
64
+ const langDirPath = path.resolve(process.cwd(), '../../lang');
65
+ const enJsonFilePath = path.resolve(langDirPath, 'en.json');
66
+
67
+ // Check if the config file already exists
68
+ if (!fs.existsSync(configFilePath)) {
69
+ // Write the config file
70
+ fs.writeFileSync(configFilePath, configContent);
71
+ console.log('ripal-ui.config.js has been generated in the root project.');
72
+
73
+ fs.mkdirSync(langDirPath, {recursive: true});
74
+
75
+ const enContent = JSON.stringify({ say_hello: "Hello world"}, null, 4);
76
+ fs.writeFileSync(enJsonFilePath, enContent);
77
+ } else {
78
+ console.log('ripal-ui.config.js already exists. No changes made.');
79
+ }
@@ -1,24 +0,0 @@
1
- import React from "react";
2
- import { Pressable, StyleSheet } from "react-native";
3
- import config from "../config";
4
-
5
- const Circle = ({size = 24, color = config.colors.primary, children, onPress, rounded = 999}) => {
6
- return (
7
- <Pressable onPress={onPress} style={{
8
- ...styles.area,
9
- backgroundColor: color,
10
- height: size,
11
- borderRadius: rounded
12
- }}>{children}</Pressable>
13
- )
14
- }
15
-
16
- const styles = StyleSheet.create({
17
- area: {
18
- aspectRatio: 1,
19
- alignItems: 'center',
20
- justifyContent: 'center',
21
- }
22
- })
23
-
24
- export default Circle;
@@ -1,71 +0,0 @@
1
- import React, { useState } from "react";
2
- import { Pressable, ScrollView, StyleSheet, View } from "react-native";
3
- import Text from "../elements/Text";
4
- import Inline from "../elements/Inline";
5
- import config from "../config";
6
-
7
- const TabScreen = ({children}) => {
8
- return children;
9
- }
10
-
11
- const Tab = ({children}) => {
12
- const [index, setIndex] = useState(0);
13
-
14
- return (
15
- <View>
16
- {
17
- children.length > 3 ?
18
- <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.tab_area}>
19
- { children.map((child, c) => {
20
- let isActive = c === index;
21
- return (
22
- <Pressable key={c} style={{
23
- ...styles.tab_item,
24
- borderBottomColor: isActive ? config.colors.primary : config.colors.slate[200],
25
- }} onPress={() => setIndex(c)}>
26
- <Text
27
- color={isActive ? config.colors.primary : config.colors.slate[500]}
28
- weight={isActive ? "600SemiBold" : "400Regular"}
29
- >{child.props.title}</Text>
30
- </Pressable>
31
- )
32
- })}
33
- </ScrollView>
34
- :
35
- <Inline style={styles.tab_area} gap={0}>
36
- { children.map((child, c) => {
37
- let isActive = c === index;
38
- return (
39
- <Inline justifyContent="center" key={c} style={{
40
- ...styles.tab_item,
41
- borderBottomColor: isActive ? config.colors.primary : config.colors.slate[200],
42
- }} onPress={() => setIndex(c)}>
43
- <Text
44
- color={isActive ? config.colors.primary : config.colors.slate[500]}
45
- weight={isActive ? "600SemiBold" : "400Regular"}
46
- >{child.props.title}</Text>
47
- </Inline>
48
- )
49
- })}
50
- </Inline>
51
- }
52
- {
53
- children[index]
54
- }
55
- </View>
56
- )
57
- }
58
-
59
- const styles = StyleSheet.create({
60
- tab_area: {
61
- marginBottom: 10,
62
- },
63
- tab_item: {
64
- paddingHorizontal: 20,
65
- paddingVertical: 12,
66
- flexGrow: 1,
67
- borderBottomWidth: 1,
68
- }
69
- })
70
-
71
- export { Tab, TabScreen }
@@ -1,4 +0,0 @@
1
- export { default as Circle } from './Circle';
2
- export { default as DatePicker } from './DatePicker';
3
- export { default as Tab } from './Tab';
4
- export { default as Table } from './Table';