UNPKG

@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
// ../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