@hoddy-ui/next
Version:
Core rich react native components written in typescript, with support for expo-router
1,672 lines (1,645 loc) • 86.6 kB
JavaScript
// ../src/theme/colors.ts
var extraColors = {};
var setExtraColors = (c) => extraColors = c;
var defaultPalettes = {
primary: {
main: "#ff8800",
light: "#feffd3",
dark: "#ffaa00",
text: "#ffffff"
},
secondary: {
main: "#ff1111",
light: "#ff4433",
dark: "#dd0000",
text: "#ffffff"
},
light: {
main: "#ffffff",
light: "#ffffff",
dark: "#dddddd",
text: "#000000"
},
dark: {
main: "#000000",
light: "#777777",
dark: "#111111",
text: "#ffffff"
},
textSecondary: {
main: "#aaaaaa",
light: "#bbbbbb",
dark: "#777777",
text: "#ffffff"
},
blue: {
main: "#0099ff",
light: "#3399ff",
dark: "#002288",
text: "#ffffff"
},
info: {
main: "#0099ff",
light: "#33aaff",
dark: "#0088aa",
text: "#ffffff"
},
success: {
main: "#00aa44",
light: "#55cc33",
dark: "#006622",
text: "#ffffff"
},
warning: {
main: "#ffaa22",
light: "#ffcc77",
dark: "#ff9900",
text: "#ffffff"
},
error: {
main: "#dd2222",
light: "#ff4433",
dark: "#aa2200",
text: "#ffffff"
}
};
var lightNeutrals = {
white: { 1: "#ffffff", 2: "#f7f7f7", 3: "#eeeeee", 4: "#dddddd", 5: "#bbbbbb" },
black: { 1: "#888888", 2: "#777777", 3: "#555555", 4: "#333333", 5: "#000000" }
};
var darkNeutrals = {
white: { 1: "#060606", 2: "#222222", 3: "#333333", 4: "#444444", 5: "#555555" },
black: { 1: "#ffffff", 2: "#f7f7f7", 3: "#eeeeee", 4: "#dddddd", 5: "#aaaaaa" }
};
var darkPaletteOverrides = {
dark: {
main: "#f2f3f4",
light: "#ffffff",
dark: "#dddddd",
text: "#000000"
},
light: {
main: "#111111",
light: "#555555",
dark: "#333333",
text: "#ffffff"
},
textSecondary: {
main: "#666666",
light: "#777777",
dark: "#444444",
text: "#ffffff"
}
};
function colors(theme) {
const isDark = theme === "dark";
const neutrals = isDark ? darkNeutrals : lightNeutrals;
const overrides = isDark ? darkPaletteOverrides : {};
const baseDefaults = {
...neutrals,
...defaultPalettes
};
const keys = /* @__PURE__ */ new Set([
...Object.keys(baseDefaults),
...Object.keys(extraColors.light ?? {}),
...Object.keys(extraColors.dark ?? {})
]);
const themeInvertingKeys = /* @__PURE__ */ new Set([
"white",
"black",
"textSecondary",
"dark",
"light"
]);
const result = {};
keys.forEach((key) => {
const skipLightExtras = isDark && themeInvertingKeys.has(key);
result[key] = {
...baseDefaults[key],
...overrides[key],
...skipLightExtras ? {} : extraColors.light?.[key],
...isDark ? extraColors.dark?.[key] : {}
};
});
return result;
}
// ../src/config/KeyManager.ts
var config = {
GOOGLE_MAP_API_KEY: "",
EDGE_TO_EDGE: false
};
function setConfig(key) {
config = key;
}
function getConfig() {
return config;
}
// ../src/config/index.ts
function initialize(config2) {
try {
setConfig({
GOOGLE_MAP_API_KEY: config2.googleMapApiKey,
TYPOGRAPHY: config2.typography,
EDGE_TO_EDGE: config2.edgeToEdge ?? false
});
if (config2.colors)
setExtraColors(config2.colors);
} catch (error) {
console.error("Error reading the config file:", error);
}
}
// components/AdaptiveStatusBarNext.tsx
import { useFocusEffect } from "expo-router";
import React4 from "react";
import { Platform as Platform3, StatusBar } from "react-native";
// ../src/hooks.ts
import { useContext } from "react";
import { Platform as Platform2, useColorScheme as useColorScheme2 } from "react-native";
// ../src/theme/index.tsx
import AsyncStorage from "@react-native-async-storage/async-storage";
import * as NavigationBar from "expo-navigation-bar";
import * as SystemUI from "expo-system-ui";
import React3, { createContext, useEffect as useEffect2, useReducer } from "react";
import { Appearance, Platform, useColorScheme } from "react-native";
import { SafeAreaProvider } from "react-native-safe-area-context";
// ../src/Components/FlashMessage.tsx
import React2, { useEffect, useRef, useState } from "react";
import { TouchableOpacity, View } from "react-native";
import Animated, {
runOnJS,
useAnimatedStyle,
useSharedValue,
withTiming
} from "react-native-reanimated";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { ScaledSheet } from "react-native-size-matters";
// ../src/Components/Typography.tsx
import React, { forwardRef } from "react";
import { StyleSheet, Text } from "react-native";
import { ms } from "react-native-size-matters";
// ../src/utility.ts
var getFontFamily = (fontWeight) => {
return getConfig().TYPOGRAPHY?.fontWeights?.[fontWeight] || getConfig().TYPOGRAPHY?.fontFamily || void 0;
};
// ../src/Components/Typography.tsx
var DEFAULT_FONT_SIZES = {
h1: ms(42),
h2: ms(37),
h3: ms(32),
h4: ms(27),
h5: ms(22),
h6: ms(17),
body1: ms(15),
body2: ms(12),
caption: ms(10)
};
var Typography = forwardRef(
({
children,
color = "dark",
style = {},
textCase,
variant = "body1",
align = "left",
gutterBottom = 0,
adjustsFontSizeToFit,
fontWeight = 400,
fontFamily,
// NEW PROP ADDED
fontSize,
lineHeight,
...props
}, ref) => {
const colors2 = useColors();
const config2 = getConfig();
const customFontSizes = config2.TYPOGRAPHY?.fontSizes;
const baseFontSize = customFontSizes?.[variant] ?? DEFAULT_FONT_SIZES[variant];
const f = fontSize || style?.fontSize || baseFontSize;
const lh = lineHeight || f * 1.2;
const styles = StyleSheet.create({
text: {
lineHeight: lh,
fontSize: f,
marginBottom: ms(gutterBottom) || 0,
color: colors2[color]?.main || color,
textTransform: textCase,
alignItems: "center",
textAlign: align,
fontWeight,
fontFamily: fontFamily || getFontFamily(fontWeight)
}
});
return /* @__PURE__ */ React.createElement(
Text,
{
ref,
adjustsFontSizeToFit,
style: [styles.text, style],
...props
},
children
);
}
);
var Typography_default = Typography;
// ../src/Components/FlashMessage.tsx
var flashListeners = /* @__PURE__ */ new Set();
var showFlashMessage = (msg) => {
flashListeners.forEach((listener) => listener(msg));
};
var subscribeToFlashMessages = (listener) => {
flashListeners.add(listener);
return () => {
flashListeners.delete(listener);
};
};
var FlashMessage = () => {
const { top } = useSafeAreaInsets();
const [message, setMessage] = useState(null);
const colors2 = useColors();
const type = message?.type || "success";
const timeoutRef = useRef(null);
const translateY = useSharedValue(-200);
const opacity = useSharedValue(0);
const hideMessage = () => {
setMessage(null);
};
const closeMessage = () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
translateY.value = withTiming(-200, { duration: 300 });
opacity.value = withTiming(0, { duration: 300 }, () => {
runOnJS(hideMessage)();
});
};
useEffect(() => {
const listener = (msg) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
translateY.value = -200;
opacity.value = 0;
setMessage(msg);
translateY.value = withTiming(0, { duration: 300 });
opacity.value = withTiming(1, { duration: 300 });
const duration = msg.duration || 3e3;
timeoutRef.current = setTimeout(() => {
translateY.value = withTiming(-200, { duration: 300 });
opacity.value = withTiming(0, { duration: 300 }, () => {
runOnJS(hideMessage)();
});
timeoutRef.current = null;
}, duration);
};
const unsubscribe = subscribeToFlashMessages(listener);
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
unsubscribe();
};
}, []);
const animatedStyle = useAnimatedStyle(() => {
return {
transform: [{ translateY: translateY.value }],
opacity: opacity.value
};
});
const textColor = type === "default" ? "#fff" : colors2[type].text;
const styles = ScaledSheet.create({
root: {
position: "absolute",
top: 0,
zIndex: 1e3,
left: 0,
paddingTop: top + 10,
paddingHorizontal: "15@ms",
backgroundColor: type === "default" ? "#333" : colors2[type].main,
width: "100%",
borderBottomLeftRadius: 10,
borderBottomRightRadius: 10,
paddingBottom: "15@ms"
},
action: {
borderRadius: 20,
marginTop: "10@vs",
flexDirection: "row",
justifyContent: "center",
paddingHorizontal: "20@ms",
paddingVertical: "8@vs",
backgroundColor: "#fff3"
}
});
if (!message)
return null;
return /* @__PURE__ */ React2.createElement(Animated.View, { style: [styles.root, animatedStyle] }, /* @__PURE__ */ React2.createElement(TouchableOpacity, { onPress: closeMessage, activeOpacity: 0.9 }, /* @__PURE__ */ React2.createElement(View, { style: { flexDirection: "row" } }, /* @__PURE__ */ React2.createElement(View, { style: { flex: 1, marginRight: 10 } }, message?.title && /* @__PURE__ */ React2.createElement(
Typography_default,
{
variant: "h6",
fontWeight: 600,
gutterBottom: 3,
color: textColor
},
message?.title
), /* @__PURE__ */ React2.createElement(Typography_default, { color: textColor }, message?.message)))), message?.actions?.map((cur, i) => /* @__PURE__ */ React2.createElement(
TouchableOpacity,
{
key: i,
style: styles.action,
onPress: () => {
cur.onPress?.();
closeMessage();
}
},
/* @__PURE__ */ React2.createElement(Typography_default, { fontWeight: 700, style: { color: "#fff" } }, cur.title)
)));
};
var FlashMessage_default = FlashMessage;
// ../src/theme/index.tsx
var UIThemeContext = createContext({
themeState: { mode: "default", value: "light" }
});
function themeReducer(state, { type, payload }) {
AsyncStorage.setItem("theme", type);
switch (type) {
case "dark":
return { mode: "dark", value: "dark" };
case "default":
return { mode: "default", value: payload };
case "light":
return { mode: "light", value: "light" };
default:
return state;
}
}
var ConfigureSystemUI = () => {
const theme = useTheme();
const colors2 = useColors();
useEffect2(() => {
const config2 = getConfig();
if (colors2) {
SystemUI.setBackgroundColorAsync(colors2.white[1]);
if (Platform.OS === "android") {
if (config2.EDGE_TO_EDGE) {
if (theme === "dark") {
NavigationBar.setStyle("light");
} else {
NavigationBar.setStyle("dark");
}
} else {
NavigationBar.setBackgroundColorAsync(colors2.white[1]);
if (theme === "dark") {
NavigationBar.setButtonStyleAsync("light");
} else {
NavigationBar.setButtonStyleAsync("dark");
}
}
}
}
}, [colors2, theme]);
return /* @__PURE__ */ React3.createElement(React3.Fragment, null);
};
var UIThemeProvider = ({ children }) => {
const [themeState, themeDispatch] = useReducer(themeReducer, {
mode: "default",
value: "light"
});
const colorScheme = useColorScheme();
React3.useEffect(() => {
AsyncStorage.getItem("theme").then((val) => {
if (val) {
if (val === "default") {
themeDispatch({
type: "default",
payload: colorScheme
});
} else {
themeDispatch({
type: val
});
Appearance.setColorScheme(val);
}
} else {
themeDispatch({
type: "default",
payload: colorScheme
});
}
});
}, [colorScheme]);
return /* @__PURE__ */ React3.createElement(SafeAreaProvider, null, /* @__PURE__ */ React3.createElement(
UIThemeContext.Provider,
{
value: {
themeState,
themeDispatch
}
},
children,
/* @__PURE__ */ React3.createElement(FlashMessage_default, null),
/* @__PURE__ */ React3.createElement(ConfigureSystemUI, null)
));
};
// ../src/hooks.ts
var useColors = () => {
const { themeState } = useContext(UIThemeContext);
return colors(themeState.value);
};
var useTheme = () => {
const { themeState } = useContext(UIThemeContext);
return themeState.value;
};
var useThemeContext = () => {
const { themeState: theme, themeDispatch } = useContext(UIThemeContext);
const colorScheme = useColorScheme2();
const setTheme = (theme2) => {
if (theme2 === "default") {
themeDispatch?.({ type: "default", payload: colorScheme });
} else {
themeDispatch?.({ type: theme2 });
}
};
return { theme, setTheme };
};
var useNavScreenOptions = (type) => {
const colors2 = useColors();
const options = {
stack: {
headerShown: false,
headerStyle: {
backgroundColor: colors2.white[1]
},
headerShadowVisible: false,
contentStyle: {
backgroundColor: colors2.white[1]
},
headerTitleStyle: {
color: colors2.black[1]
},
headerTintColor: Platform2.OS === "android" ? colors2.black[1] : colors2.blue.light
},
tab: {
headerShown: false,
headerTintColor: colors2.dark.main,
tabBarStyle: {
borderTopColor: colors2.white[2],
borderColor: colors2.white[2],
borderTopWidth: 1,
backgroundColor: colors2.white[1]
},
tabBarActiveTintColor: colors2.primary.main,
tabBarInactiveTintColor: colors2.textSecondary.main,
tabBarLabelStyle: {
// fontSize: ms(12),
}
},
drawer: {
headerShown: false,
drawerActiveTintColor: colors2.primary.main,
drawerInactiveTintColor: colors2.textSecondary.main,
sceneContainerStyle: {
backgroundColor: colors2.white[2]
},
drawerStyle: {
backgroundColor: colors2.white[1]
},
headerStyle: {
backgroundColor: colors2.white[1]
},
headerTitleStyle: {
color: colors2.dark.main
}
}
};
return options[type];
};
// components/AdaptiveStatusBarNext.tsx
var AdaptiveStatusBar = ({ translucent = false }) => {
const colors2 = useColors();
const theme = useTheme();
const statusbarHandler = () => {
StatusBar.setBarStyle(theme === "dark" ? "light-content" : "dark-content");
if (Platform3.OS === "android") {
StatusBar.setTranslucent(true);
}
};
useFocusEffect(
React4.useCallback(() => {
statusbarHandler();
}, [theme])
);
React4.useEffect(() => {
statusbarHandler();
}, [theme]);
return /* @__PURE__ */ React4.createElement(React4.Fragment, null);
};
var AdaptiveStatusBarNext_default = AdaptiveStatusBar;
// ../src/Components/AlertX.tsx
import { MaterialIcons } from "@expo/vector-icons";
import React5 from "react";
import { View as View2 } from "react-native";
import { ms as ms2, ScaledSheet as ScaledSheet2 } from "react-native-size-matters";
var AlertX = ({
type = "info",
variant = "contained",
title,
gutterBottom = 0,
body,
style = {}
}) => {
const colors2 = useColors();
const styles = ScaledSheet2.create({
container: {
padding: 20,
paddingTop: 10,
paddingBottom: 10,
borderRadius: 8,
alignItems: "center",
flexDirection: "row",
marginBottom: ms2(gutterBottom),
backgroundColor: colors2[type].main + (variant === "contained" ? "" : "3")
},
title: {
color: variant === "contained" ? "#fff" : colors2[type].main
},
body: {
color: variant === "contained" ? "#fff" : colors2[type].main
}
});
return /* @__PURE__ */ React5.createElement(View2, { style: [styles.container, style] }, /* @__PURE__ */ React5.createElement(View2, { style: { width: "80%" } }, /* @__PURE__ */ React5.createElement(Typography_default, { style: styles.title, gutterBottom: 3, fontWeight: 700 }, title), body && /* @__PURE__ */ React5.createElement(Typography_default, { fontWeight: 700, variant: "body2", style: styles.body }, body)), /* @__PURE__ */ React5.createElement(View2, { style: { marginLeft: "auto" } }, /* @__PURE__ */ React5.createElement(
MaterialIcons,
{
color: variant === "contained" ? "#fff" : colors2[type].main,
size: 36,
name: type === "success" ? "check" : type
}
)));
};
var AlertX_default = AlertX;
// ../src/Components/Avatar.tsx
import { Ionicons } from "@expo/vector-icons";
import React6, { useState as useState3 } from "react";
import { Image, View as View3 } from "react-native";
import { ms as ms3, ScaledSheet as ScaledSheet3 } from "react-native-size-matters";
var Avatar = ({
color = "dark",
label,
variant = "contained",
source,
size = 48,
style = {},
icon
}) => {
const colors2 = useColors();
const [imageError, setImageError] = useState3(false);
const styles = ScaledSheet3.create({
root: {
borderRadius: 150,
height: ms3(size),
width: ms3(size),
alignItems: "center",
justifyContent: "center",
overflow: "hidden",
borderWidth: variant === "outlined" ? 5 : 0,
borderColor: variant === "outlined" ? "#fff" : "#0000",
backgroundColor: variant === "outlined" ? void 0 : label ? colors2[color].main : colors2.white[4],
...style
},
image: {
height: "110%",
width: "110%"
}
});
return /* @__PURE__ */ React6.createElement(View3, { style: styles.root }, source && !imageError ? /* @__PURE__ */ React6.createElement(
Image,
{
resizeMode: "cover",
style: styles.image,
source,
onError: () => setImageError(true)
}
) : label ? /* @__PURE__ */ React6.createElement(Typography_default, { style: { color: colors2[color].text } }, label[0]) : icon ? icon : /* @__PURE__ */ React6.createElement(Ionicons, { name: "person", color: "#fff", size: Math.round(size / 1.5) }));
};
var Avatar_default = Avatar;
// ../src/Components/Button.tsx
import { Ionicons as Ionicons2, MaterialIcons as MaterialIcons2 } from "@expo/vector-icons";
import React7, { forwardRef as forwardRef2 } from "react";
import { ActivityIndicator, Text as Text2, TouchableOpacity as TouchableOpacity2 } from "react-native";
import { ScaledSheet as ScaledSheet4, moderateScale, ms as ms4 } from "react-native-size-matters";
var LinkButton = ({
title,
style = {},
color = "blue",
fontSize = 12,
fontWeight = 400,
disabled,
onPress = () => {
}
}) => {
const colors2 = useColors();
const styles = ScaledSheet4.create({
text: {
fontSize: moderateScale(fontSize),
fontWeight: fontWeight.toString(),
fontFamily: getFontFamily(fontWeight),
color: disabled ? "#777" : colors2[color].main
}
});
return /* @__PURE__ */ React7.createElement(TouchableOpacity2, { onPress, disabled }, /* @__PURE__ */ React7.createElement(Text2, { style: { ...styles.text, ...style } }, title));
};
var IconButton = ({
style = {},
color = "dark",
disabled,
icon,
elevation,
bg = false,
size = 24,
containerStyles = {},
onPress = () => {
},
iconType = "material"
}) => {
const colors2 = useColors();
const theme = useTheme();
const bgColor = theme === "light" ? "#fff" : "#222";
const styles = ScaledSheet4.create({
container: {
alignSelf: "flex-start",
flexGrow: 0,
backgroundColor: bg ? bgColor : elevation > 0 ? bgColor : void 0,
padding: "5@ms",
shadowColor: "#000",
shadowOpacity: 0.1,
shadowOffset: {
height: 1,
width: 0
},
height: bg ? ms4(size + 20) : void 0,
width: bg ? ms4(size + 20) : void 0,
alignItems: "center",
justifyContent: "center",
shadowRadius: elevation,
elevation,
borderRadius: size * 5
},
text: {
color: disabled ? "#777" : colors2[color].main
}
});
const IconComp = {
material: MaterialIcons2,
ion: Ionicons2
}[iconType];
return /* @__PURE__ */ React7.createElement(
TouchableOpacity2,
{
onPress,
activeOpacity: 0.3,
style: { ...styles.container, ...containerStyles }
},
/* @__PURE__ */ React7.createElement(IconComp, { style: { ...styles.text, ...style }, name: icon, size })
);
};
var Button = forwardRef2(
({
elevation = 0,
onPress = () => {
},
disabled = false,
title,
loading,
size = "normal",
rounded = false,
gutterBottom,
style = {},
fullWidth = false,
translucent = false,
color = "primary",
variant = "contained",
start,
end
}, ref) => {
const colors2 = useColors();
const styles = ScaledSheet4.create({
con: {
flexDirection: "row",
alignItems: "center",
alignSelf: "flex-start",
justifyContent: "center",
backgroundColor: variant === "text" || variant === "outlined" ? void 0 : variant === "translucent" ? colors2[color].main + "22" : translucent ? translucent === "dark" ? colors2.white[3] + "22" : colors2.black[3] + "22" : loading ? colors2[color].light : disabled ? colors2.white[4] : colors2[color].main,
borderRadius: rounded ? 30 : 10,
elevation: variant === "text" ? 0 : elevation,
paddingVertical: size === "small" ? 8 : size === "large" ? "15@mvs" : "13@mvs",
paddingHorizontal: size === "small" ? "10@ms" : "18@ms",
borderColor: colors2[color].main,
borderWidth: variant === "outlined" ? 1 : 0,
shadowColor: "#000",
shadowRadius: elevation,
marginBottom: gutterBottom,
shadowOffset: {
height: elevation / 2,
width: 0
},
shadowOpacity: variant === "text" ? 0 : 0.3,
width: fullWidth ? "100%" : void 0,
...style
},
text: {
color: disabled ? variant === "text" || variant === "outlined" || variant === "translucent" ? colors2.black[1] : colors2[color].text : colors2[color][variant === "text" || variant === "outlined" || variant === "translucent" ? "main" : "text"],
fontWeight: variant === "outlined" ? "700" : variant === "translucent" ? "600" : "500",
fontSize: size === "small" ? "12@ms" : "13@ms",
fontFamily: getFontFamily(
variant === "outlined" ? 700 : variant === "translucent" ? 600 : 500
)
}
});
return /* @__PURE__ */ React7.createElement(
TouchableOpacity2,
{
ref,
onPress,
disabled,
style: styles.con
},
start,
loading && /* @__PURE__ */ React7.createElement(
ActivityIndicator,
{
size: "small",
color: variant === "text" || variant === "outlined" || variant === "translucent" ? colors2[color].main : colors2[color].text,
style: { marginRight: 10 }
}
),
/* @__PURE__ */ React7.createElement(Text2, { style: styles.text }, title),
end
);
}
);
var Button_default = Button;
// ../src/Components/Checkbox.tsx
import { MaterialCommunityIcons } from "@expo/vector-icons";
import React8 from "react";
import { TouchableOpacity as TouchableOpacity3, View as View4 } from "react-native";
import { ScaledSheet as ScaledSheet5 } from "react-native-size-matters";
var CheckBox = ({
color = "primary",
checked,
size = 24,
label,
style = {},
onChange
}) => {
const iconName = checked ? "checkbox-marked" : "checkbox-blank-outline";
const colors2 = useColors();
const styles = ScaledSheet5.create({
container: {
alignItems: "center",
flexDirection: "row",
...style
}
});
return /* @__PURE__ */ React8.createElement(View4, { style: styles.container }, /* @__PURE__ */ React8.createElement(TouchableOpacity3, { onPress: onChange }, /* @__PURE__ */ React8.createElement(
MaterialCommunityIcons,
{
name: iconName,
size,
color: colors2[color].main
}
)), label);
};
// ../src/Components/FormWrapper.tsx
import React9, { forwardRef as forwardRef3 } from "react";
import {
Keyboard,
KeyboardAvoidingView,
Platform as Platform4,
ScrollView,
TouchableWithoutFeedback
} from "react-native";
import { useSafeAreaInsets as useSafeAreaInsets2 } from "react-native-safe-area-context";
import { ScaledSheet as ScaledSheet6 } from "react-native-size-matters";
var FormWrapper = forwardRef3(
({
children,
behavior = Platform4.OS === "ios" ? "padding" : "height",
contentContainerStyle,
mode = "scroll",
keyboardVerticalOffset = 10,
style = {},
onScroll
}, ref) => {
const { bottom } = useSafeAreaInsets2();
const defaultOffset = Platform4.OS === "ios" ? -bottom : -bottom * 2;
const styles = ScaledSheet6.create({
root: {
width: "100%",
flex: 1,
...style
}
});
return mode === "static" ? /* @__PURE__ */ React9.createElement(TouchableWithoutFeedback, { onPress: Keyboard.dismiss, accessible: false }, /* @__PURE__ */ React9.createElement(
KeyboardAvoidingView,
{
style: styles.root,
behavior,
contentContainerStyle: styles.root,
keyboardVerticalOffset: keyboardVerticalOffset || defaultOffset
},
children
)) : /* @__PURE__ */ React9.createElement(
KeyboardAvoidingView,
{
behavior,
style: styles.root,
keyboardVerticalOffset: keyboardVerticalOffset || defaultOffset
},
/* @__PURE__ */ React9.createElement(
ScrollView,
{
ref,
onScroll,
showsVerticalScrollIndicator: false,
scrollEventThrottle: 40,
keyboardDismissMode: "interactive",
contentContainerStyle,
keyboardShouldPersistTaps: "handled"
},
children
)
);
}
);
// ../src/Components/StarRating.tsx
import { Ionicons as Ionicons3 } from "@expo/vector-icons";
import * as Haptics from "expo-haptics";
import React11, { useEffect as useEffect4, useState as useState5 } from "react";
import {
ActivityIndicator as ActivityIndicator2,
TextInput,
TouchableOpacity as TouchableOpacity4,
View as View6
} from "react-native";
import { ScaledSheet as ScaledSheet8 } from "react-native-size-matters";
// ../src/Components/Popup.tsx
import {
Dimensions,
Keyboard as Keyboard2,
KeyboardAvoidingView as KeyboardAvoidingView2,
Modal,
Platform as Platform5,
Pressable,
StyleSheet as StyleSheet2,
TouchableWithoutFeedback as TouchableWithoutFeedback2,
View as View5
} from "react-native";
import React10, { useEffect as useEffect3, useState as useState4 } from "react";
import Animated2, {
LinearTransition,
runOnJS as runOnJS2,
useAnimatedStyle as useAnimatedStyle2,
useSharedValue as useSharedValue2,
withTiming as withTiming2
} from "react-native-reanimated";
import { useSafeAreaInsets as useSafeAreaInsets3 } from "react-native-safe-area-context";
import { ms as ms5, ScaledSheet as ScaledSheet7 } from "react-native-size-matters";
var Popup = ({
title,
sheet = true,
bare = false,
keyboardVerticalOffset,
children,
open,
onClose = () => {
},
style,
onModalShow,
onModalHide,
disableAutoKeyboardManagement = false
}) => {
const theme = useTheme();
const colors2 = useColors();
const [modalVisible, setModalVisible] = useState4(false);
const [modalOpen, setModalOpen] = useState4(false);
const [keyboardVisible, setKeyboardVisible] = useState4(false);
const { bottom } = useSafeAreaInsets3();
const backdropOpacity = useSharedValue2(0);
const contentTranslateY = useSharedValue2(1e3);
const keyboardVerticalOffsetValue = Platform5.OS === "ios" ? -bottom : -bottom * 2;
useEffect3(() => {
const keyboardDidShowListener = Keyboard2.addListener(
"keyboardDidShow",
() => {
setKeyboardVisible(true);
}
);
const keyboardDidHideListener = Keyboard2.addListener(
"keyboardDidHide",
() => {
setKeyboardVisible(false);
}
);
return () => {
keyboardDidHideListener?.remove();
keyboardDidShowListener?.remove();
};
}, []);
const _onModalShow = () => {
setModalVisible(true);
onModalShow?.();
};
const _onModalHide = () => {
onModalHide?.();
setModalOpen(false);
};
useEffect3(() => {
if (open) {
setModalOpen(true);
backdropOpacity.value = withTiming2(1, { duration: 300 });
contentTranslateY.value = withTiming2(0, { duration: 300 }, () => {
runOnJS2(_onModalShow)();
});
} else {
setModalVisible(false);
backdropOpacity.value = withTiming2(0, { duration: 200 });
contentTranslateY.value = withTiming2(1e3, { duration: 200 }, () => {
runOnJS2(_onModalHide)();
});
}
}, [open]);
const backdropAnimatedStyle = useAnimatedStyle2(() => ({
opacity: backdropOpacity.value
}));
const contentAnimatedStyle = useAnimatedStyle2(() => ({
transform: [{ translateY: contentTranslateY.value }]
}));
const styles = ScaledSheet7.create({
root: {
height: "100%",
width: "100%",
justifyContent: sheet ? "flex-end" : "center"
},
keyboardView: {
flex: 1,
zIndex: 1e3
},
avoidingView: {
zIndex: 2,
minHeight: typeof sheet === "number" ? sheet : void 0,
maxHeight: "90%",
alignSelf: "center",
maxWidth: sheet ? void 0 : "90%",
width: sheet ? "100%" : void 0,
marginBottom: Platform5.OS === "android" && keyboardVisible ? bottom : 0
},
container: {
paddingBottom: sheet && !bare ? bottom + ms5(0) : void 0,
backgroundColor: theme === "dark" ? "#111" : colors2.white[1],
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
borderBottomRightRadius: sheet ? 0 : 20,
borderBottomLeftRadius: sheet ? 0 : 20,
width: "100%",
overflow: "hidden",
...style
},
content: {
maxHeight: sheet && !bare ? Dimensions.get("screen").height * 0.9 - bottom - ms5(60) : void 0,
paddingHorizontal: bare ? void 0 : "15@ms"
// backgroundColor : "#f94",
},
title: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
height: "50@ms"
},
titleIcon: {
position: "absolute",
left: "15@ms"
},
backdrop: {
position: "absolute",
height: "100%",
zIndex: 1,
width: "100%",
backgroundColor: "#000b"
}
});
const closeAction = () => {
onClose();
};
return /* @__PURE__ */ React10.createElement(
Modal,
{
transparent: true,
animationType: "none",
statusBarTranslucent: true,
visible: modalOpen,
onRequestClose: closeAction,
navigationBarTranslucent: true
},
/* @__PURE__ */ React10.createElement(UIThemeProvider, null, /* @__PURE__ */ React10.createElement(Animated2.View, { style: [styles.backdrop, backdropAnimatedStyle] }), /* @__PURE__ */ React10.createElement(
KeyboardAvoidingView2,
{
style: styles.keyboardView,
behavior: Platform5.OS === "ios" ? "padding" : "height",
keyboardVerticalOffset: keyboardVerticalOffset || keyboardVerticalOffsetValue
},
/* @__PURE__ */ React10.createElement(
TouchableWithoutFeedback2,
{
onPress: Keyboard2.dismiss,
disabled: disableAutoKeyboardManagement
},
/* @__PURE__ */ React10.createElement(View5, { style: styles.root }, modalOpen && /* @__PURE__ */ React10.createElement(
Pressable,
{
style: [StyleSheet2.absoluteFill, { zIndex: 1 }],
onPress: closeAction
}
), /* @__PURE__ */ React10.createElement(
Animated2.View,
{
style: [styles.avoidingView, contentAnimatedStyle],
layout: modalVisible ? LinearTransition.springify().stiffness(200).mass(0.5).damping(100) : void 0
},
/* @__PURE__ */ React10.createElement(View5, { style: styles.container }, !bare && /* @__PURE__ */ React10.createElement(View5, { style: styles.title }, /* @__PURE__ */ React10.createElement(View5, { style: styles.titleIcon }, /* @__PURE__ */ React10.createElement(
IconButton,
{
size: 20,
icon: "close",
onPress: closeAction
}
)), /* @__PURE__ */ React10.createElement(Typography_default, { align: "center", fontWeight: 500 }, title)), /* @__PURE__ */ React10.createElement(View5, { style: styles.content }, children))
))
)
))
);
};
// ../src/Components/StarRating.tsx
var RatingInput = ({
onSubmit: _onSubmit,
rating = 0,
size = 16,
color = "primary"
}) => {
const [showReviewsModal, setShowReviewsModal] = useState5(false);
const [rate, setRate] = useState5(0);
const colors2 = useColors();
const [loading, setLoading] = useState5(false);
const [review, setReview] = useState5("");
const styles = ScaledSheet8.create({
root: {
flexDirection: "row",
alignItems: "center"
},
inputCon: {
marginBottom: "20@vs",
backgroundColor: colors2.white[3],
padding: "15@ms",
borderRadius: 20
},
input: {
fontSize: "16@ms",
color: colors2.dark.main,
height: "100@vs"
}
});
useEffect4(() => {
setRate(rating);
}, [rating]);
const onRate = (index) => {
setRate(index + 1);
Haptics.selectionAsync();
setTimeout(() => {
setShowReviewsModal(true);
}, 500);
};
const onSubmit = async () => {
setLoading(true);
setShowReviewsModal(false);
_onSubmit && await _onSubmit({ rating: rate, review });
setLoading(false);
};
return /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(View6, { style: styles.root }, loading ? /* @__PURE__ */ React11.createElement(ActivityIndicator2, null) : [...Array(5)].map((_, index) => /* @__PURE__ */ React11.createElement(
TouchableOpacity4,
{
key: index,
activeOpacity: 0.9,
onPress: () => {
onRate(index);
}
},
/* @__PURE__ */ React11.createElement(
Ionicons3,
{
style: { marginLeft: 10 },
name: index < rate ? "star" : "star-outline",
size,
color: colors2[color]?.main || color
}
)
))), /* @__PURE__ */ React11.createElement(
Popup,
{
sheet: true,
open: showReviewsModal,
onClose: () => {
setShowReviewsModal(false);
}
},
/* @__PURE__ */ React11.createElement(
View6,
{
style: {
alignItems: "center",
marginBottom: 5
}
},
/* @__PURE__ */ React11.createElement(RatingStars, { rating: rate, size: 24 })
),
/* @__PURE__ */ React11.createElement(
Typography_default,
{
align: "center",
fontWeight: 700,
variant: "h5",
gutterBottom: 20
},
"Add to your review"
),
/* @__PURE__ */ React11.createElement(View6, { style: styles.inputCon }, /* @__PURE__ */ React11.createElement(
TextInput,
{
style: styles.input,
multiline: true,
value: review,
onChangeText: (text) => setReview(text),
placeholder: "Type review here..",
verticalAlign: "top"
}
)),
/* @__PURE__ */ React11.createElement(
Button_default,
{
gutterBottom: 40,
title: "Submit Review",
loading,
disabled: loading,
onPress: () => {
onSubmit();
}
}
)
));
};
var RatingStars = ({
rating = 0,
size = 16,
color = "#FFD700",
inactiveColor = "textSecondary"
}) => {
const colors2 = useColors();
const styles = ScaledSheet8.create({
root: {
flexDirection: "row",
alignItems: "center"
}
});
return /* @__PURE__ */ React11.createElement(View6, { style: styles.root }, [...Array(Math.floor(rating))].map((_, index) => /* @__PURE__ */ React11.createElement(
Ionicons3,
{
key: index,
name: "star",
size,
color: colors2[color]?.main || color
}
)), [...Array(5 - Math.floor(rating))].map((_, index) => /* @__PURE__ */ React11.createElement(
Ionicons3,
{
key: index,
name: "star",
size,
color: colors2[inactiveColor]?.main || inactiveColor
}
)));
};
// ../src/Components/Grid.tsx
import React12 from "react";
import { View as View7 } from "react-native";
import { ms as ms6, ScaledSheet as ScaledSheet9 } from "react-native-size-matters";
var GridItem = ({
children,
col = 2,
alignItems,
spacing = 1,
style = {}
}) => {
const styles = ScaledSheet9.create({
gridItem: {
width: `${100 / col}%`,
padding: ms6(spacing * 10),
alignItems
}
});
return /* @__PURE__ */ React12.createElement(View7, { children, style: [styles.gridItem, style] });
};
var Grid = ({
children,
spacing = 1,
style = {}
}) => {
const styles = ScaledSheet9.create({
grid: {
flexWrap: "wrap",
margin: `${-spacing * 10}@ms`,
flexDirection: "row"
}
});
return /* @__PURE__ */ React12.createElement(View7, { children, style: [styles.grid, style] });
};
// ../src/Components/Locator.tsx
import { Ionicons as Ionicons5 } from "@expo/vector-icons";
import React16, { useEffect as useEffect5, useState as useState8 } from "react";
import { Alert, TouchableOpacity as TouchableOpacity8, View as View11 } from "react-native";
// ../src/Components/List.tsx
import { MaterialIcons as MaterialIcons3 } from "@expo/vector-icons";
import React13 from "react";
import { TouchableOpacity as TouchableOpacity5, View as View8 } from "react-native";
import { ScaledSheet as ScaledSheet10 } from "react-native-size-matters";
var ListItem = ({
link = false,
divider = false,
onPress,
index = 1,
style = {},
children
}) => {
const colors2 = useColors();
const styles = ScaledSheet10.create({
root: {
flexDirection: "row",
alignItems: "center",
paddingHorizontal: "10@s",
borderBottomColor: colors2.white[2],
borderBottomWidth: divider ? 1 : 0,
paddingVertical: "10@vs"
}
});
return /* @__PURE__ */ React13.createElement(
View8,
null,
/* @__PURE__ */ React13.createElement(TouchableOpacity5, { disabled: Boolean(!onPress), onPress }, /* @__PURE__ */ React13.createElement(View8, { style: { ...styles.root, ...style } }, children, link && /* @__PURE__ */ React13.createElement(
MaterialIcons3,
{
color: colors2.white[5],
style: { marginLeft: "auto" },
name: "arrow-forward-ios",
size: 15
}
)))
);
};
// ../src/Components/TextField.tsx
import { Ionicons as Ionicons4, MaterialIcons as MaterialIcons5 } from "@expo/vector-icons";
import React15, { useRef as useRef2, useState as useState7 } from "react";
import {
Animated as Animated3,
Keyboard as Keyboard3,
TextInput as TextInput2,
TouchableOpacity as TouchableOpacity7,
View as View10
} from "react-native";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import {
ScaledSheet as ScaledSheet12,
moderateScale as moderateScale2,
ms as ms7,
verticalScale
} from "react-native-size-matters";
// ../src/Components/SelectMenu.tsx
import { MaterialIcons as MaterialIcons4 } from "@expo/vector-icons";
import React14, { useCallback, useState as useState6 } from "react";
import { FlatList, TouchableOpacity as TouchableOpacity6, View as View9 } from "react-native";
import { useSafeAreaInsets as useSafeAreaInsets4 } from "react-native-safe-area-context";
import { ScaledSheet as ScaledSheet11 } from "react-native-size-matters";
var SelectMenu = ({
open = false,
onClose,
value,
options = [],
onChange,
disableAutoClose = false,
label,
secondary,
helperText,
searchEnabled = false,
searchPlaceholder = "Search"
}) => {
const colors2 = useColors();
const { bottom } = useSafeAreaInsets4();
const [search, setSearch] = useState6("");
const styles = ScaledSheet11.create({
header: {
marginBottom: "20@vs"
},
option: {
paddingHorizontal: "10@s",
paddingVertical: "10@vs",
borderRadius: 8,
flexDirection: "row",
alignItems: "center",
marginBottom: "10@vs"
}
});
const renderItem = useCallback(
({ item }) => /* @__PURE__ */ React14.createElement(
TouchableOpacity6,
{
style: {
...styles.option,
backgroundColor: item.value === value ? colors2.blue.light + "22" : colors2.white[2]
},
onPress: () => {
onChange(item.value);
if (!disableAutoClose)
onClose();
},
key: item.label
},
item.start && /* @__PURE__ */ React14.createElement(View9, { style: { marginRight: 10 } }, item.start),
/* @__PURE__ */ React14.createElement(View9, { style: { flex: 1 } }, /* @__PURE__ */ React14.createElement(
Typography_default,
{
variant: "body2",
style: {
color: item.value === value ? colors2.blue.light : colors2.black[2]
}
},
item.label
), item.secondary ? /* @__PURE__ */ React14.createElement(
Typography_default,
{
variant: "body2",
style: {
marginTop: 2,
color: item.value === value ? colors2.blue.light : colors2.white[5]
}
},
item.secondary
) : null),
value === item.value && /* @__PURE__ */ React14.createElement(
MaterialIcons4,
{
name: "check",
color: colors2.blue.light,
size: 24,
style: { marginLeft: "auto" }
}
)
),
[value, colors2]
);
return /* @__PURE__ */ React14.createElement(
Popup,
{
open,
onClose,
title: label,
disableAutoKeyboardManagement: true
},
/* @__PURE__ */ React14.createElement(View9, { style: styles.content }, /* @__PURE__ */ React14.createElement(
FlatList,
{
removeClippedSubviews: true,
keyExtractor: (item) => item.value,
bounces: false,
renderItem,
ListHeaderComponent: /* @__PURE__ */ React14.createElement(View9, { style: styles.header }, helperText && /* @__PURE__ */ React14.createElement(
Typography_default,
{
variant: "body2",
color: "textSecondary",
gutterBottom: 5
},
helperText
), searchEnabled && /* @__PURE__ */ React14.createElement(
TextField_default,
{
label: searchPlaceholder,
value: search,
type: "search",
onChangeText: setSearch,
variant: "outlined"
}
)),
data: options.filter(
(item) => search.length > 1 ? item.label.toLowerCase().indexOf(search.toLowerCase()) > -1 : item
).sort((a, b) => a.label.localeCompare(b.label))
}
))
);
};
var SelectMenu_default = SelectMenu;
// ../src/Components/TextField.tsx
var formPropsFromType = (type, showPassword) => type === "email" ? {
textContentType: "emailAddress",
keyboardType: "email-address",
autoCapitalize: "none",
autoCompleteType: "email"
} : type === "number" ? { keyboardType: "numeric" } : type === "tel" ? {
textContentType: "telephoneNumber",
keyboardType: "phone-pad"
} : type === "search" ? {
keyboardType: "web-search",
returnKeyType: "search",
autoCapitalize: "none"
} : type === "password" ? {
secureTextEntry: !showPassword,
autoCompleteType: "password",
autoCapitalize: "none",
textContentType: "password"
} : {};
function parseDateValue(value) {
if (!value)
return /* @__PURE__ */ new Date();
if (value instanceof Date)
return value;
const isoParts = `${value}`.split("-");
if (isoParts.length === 3) {
const [year, month, day] = isoParts;
const parsed = new Date(
parseInt(year, 10),
parseInt(month, 10) - 1,
parseInt(day, 10)
);
if (!isNaN(parsed.getTime()))
return parsed;
}
const fallback = new Date(value);
return isNaN(fallback.getTime()) ? /* @__PURE__ */ new Date() : fallback;
}
var TextFieldBase = React15.forwardRef(
({
label,
labelProps,
labelVariant,
keyboardType,
variant = "outlined",
color = "primary",
value,
type,
placeholder = "",
helperText,
onChangeText,
onSubmitEditing = () => {
},
onFocus = () => {
},
onBlur = () => {
},
error,
start,
size = "normal",
rounded,
disabled = false,
style = {},
inputStyles = {},
gutterBottom,
end,
options,
multiline,
selectMenuProps,
labelAlwaysOpen,
...props
}, ref) => {
const colors2 = useColors();
const theme = useTheme();
const [focused, _setFocused] = useState7(false);
const [showPassword, setShowPassword] = useState7(false);
const [datePickerVisible, setDatePickerVisible] = useState7(false);
const isDate = type === "date";
const setFocused = (next) => {
if (options && next) {
Keyboard3.dismiss();
setTimeout(() => _setFocused(next), 100);
} else {
_setFocused(next);
}
};
const isFloating = labelVariant === "floating";
const labelOpen = labelAlwaysOpen || focused || value;
const baseHeight = moderateScale2(
multiline ? 50 + (props.numberOfLines || 1) * 18 : 50
);
const sizeMultiplier = isFloating ? size === "large" ? 1.2 : size === "small" ? 0.8 : 1 : 1;
const height2 = baseHeight * sizeMultiplier;
const labelAnim = useRef2(
new Animated3.Value(
height2 / moderateScale2(variant === "text" ? 2.5 : 3.2)
)
).current;
React15.useEffect(() => {
if (!isFloating)
return;
if (labelOpen) {
Animated3.timing(labelAnim, {
toValue: verticalScale(variant === "text" ? 2 : 4),
duration: 300,
useNativeDriver: false
}).start();
} else {
Animated3.timing(labelAnim, {
toValue: height2 / moderateScale2(variant === "text" ? 2.5 : 3.2),
duration: 300,
useNativeDriver: false
}).start();
}
}, [isFloating, labelOpen, variant, height2]);
const formProps = formPropsFromType(type, showPassword);
const handleDateConfirm = (date) => {
const year = date.getFullYear();
const month = `${date.getMonth() + 1}`.padStart(2, "0");
const day = `${date.getDate()}`.padStart(2, "0");
onChangeText?.(`${year}-${month}-${day}`);
setDatePickerVisible(false);
setFocused(false);
};
const handleContainerPress = () => {
if (disabled)
return;
setFocused(true);
if (isDate) {
onFocus();
setDatePickerVisible(true);
}
};
const inputPadding = variant === "text" ? 0 : moderateScale2(isFloating ? 15 : 10);
const containerBorderRadius = variant === "text" ? 0 : rounded ? 30 : isFloating ? 7 : 10;
const datePlaceholderColor = colors2.textSecondary.light;
const styles = ScaledSheet12.create({
root: {
marginBottom: ms7(gutterBottom ?? (isFloating ? 0 : 8)),
width: "100%",
...style
},
container: {
height: height2,
overflow: "hidden",
flexDirection: "row",
backgroundColor: variant === "outlined" || variant === "text" ? "#fff0" : focused ? colors2.white[2] + "dd" : colors2.white[2],
borderColor: error ? colors2.error.main : isFloating && focused && variant === "contained" ? colors2[color].light : focused ? colors2[color].main : colors2.textSecondary.main,
borderWidth: error ? 1 : isFloating && focused && variant === "contained" ? 1 : variant === "outlined" ? focused ? 2 : 0.5 : 0,
borderBottomWidth: variant === "text" ? 0.5 : void 0,
width: "100%",
borderRadius: containerBorderRadius,
alignItems: multiline ? "flex-start" : "center",
paddingVertical: multiline ? 10 : 0,
...inputStyles
},
input: {
fontSize: "14@ms",
flex: 1,
alignSelf: "stretch",
padding: 0,
paddingLeft: inputPadding,
lineHeight: "18@ms",
paddingRight: moderateScale2(10),
...isFloating ? { marginTop: "13@ms", fontFamily: getFontFamily(400) } : {},
color: disabled ? colors2.textSecondary.main : colors2.dark.main,
zIndex: 10
},
inputText: {
fontSize: "14@ms",
flex: 1,
paddingLeft: inputPadding,
...isFloating ? { paddingTop: "13@ms" } : {}
},
dateContent: {
flexDirection: "row",
alignItems: "center",
flex: 1,
paddingLeft: inputPadding,
paddingRight: moderateScale2(10),
paddingTop: isFloating ? variant === "text" ? ms7(13) : ms7(12) : multiline ? 4 : 0
},
dateText: { fontSize: "14@ms", flex: 1 },
datePlaceholder: { color: datePlaceholderColor },
...isFloating ? {
contentWrapper: { flex: 1, position: "relative" },
label: {
fontFamily: getFontFamily(400),
position: "absolute",
left: inputPadding,
fontSize: labelOpen ? "10@ms" : "13@ms",
color: error ? colors2.error.main : focused ? theme === "dark" ? colors2[color].light : colors2[color].main : colors2.textSecondary.main
}
} : {},
helperText: {
paddingHorizontal: "15@ms",
color: error ? colors2.error.main : focused ? theme === "dark" ? colors2[color].light : colors2[color].main