14c880123c
Expo app with dashboard, time clock, invoices, and settings — native tabs, glass UI, theme-aware components, and iOS Live Activities. Co-authored-by: Cursor <cursoragent@cursor.com>
109 lines
2.4 KiB
TypeScript
109 lines
2.4 KiB
TypeScript
import { Image } from "expo-image";
|
|
import { StyleSheet, Text, View, type ImageStyle, type ViewStyle } from "react-native";
|
|
|
|
import { useAppTheme } from "@/contexts/ThemeContext";
|
|
import { fonts } from "@/constants/theme";
|
|
|
|
type LogoSize = "xs" | "sm" | "md" | "lg";
|
|
|
|
const widths: Record<LogoSize, number> = {
|
|
xs: 104,
|
|
sm: 140,
|
|
md: 180,
|
|
lg: 220,
|
|
};
|
|
|
|
type LogoProps = {
|
|
size?: LogoSize;
|
|
style?: ViewStyle;
|
|
/** Force the light wordmark for dark backgrounds (e.g. status bar chrome). */
|
|
onDark?: boolean;
|
|
};
|
|
|
|
/** Full beenvoice wordmark from web `public/beenvoice-logo.png` */
|
|
export function Logo({ size = "md", style, onDark }: LogoProps) {
|
|
const { isDark } = useAppTheme();
|
|
const width = widths[size];
|
|
const height = width * (436 / 2970);
|
|
const useDarkAsset = onDark ?? isDark;
|
|
|
|
return (
|
|
<View style={[styles.row, styles.noShrink, style]}>
|
|
<Image
|
|
source={
|
|
useDarkAsset
|
|
? require("@/assets/images/beenvoice-logo-dark.png")
|
|
: require("@/assets/images/beenvoice-logo.png")
|
|
}
|
|
style={{ width, height }}
|
|
contentFit="contain"
|
|
/>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/** Square app icon mark — fixed aspect ratio so flex parents cannot squash it. */
|
|
export function LogoMark({
|
|
size = 32,
|
|
style,
|
|
}: {
|
|
size?: number;
|
|
style?: ImageStyle;
|
|
}) {
|
|
const flat = StyleSheet.flatten(style);
|
|
const width =
|
|
typeof flat?.width === "number"
|
|
? flat.width
|
|
: typeof flat?.height === "number"
|
|
? flat.height
|
|
: size;
|
|
const height = typeof flat?.height === "number" ? flat.height : width;
|
|
|
|
return (
|
|
<View style={[styles.markBox, { width, height }]}>
|
|
<Image
|
|
source={require("@/assets/images/icon.png")}
|
|
style={styles.markImage}
|
|
contentFit="contain"
|
|
accessibilityLabel="beenvoice"
|
|
/>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
export function HeadingText({
|
|
children,
|
|
style,
|
|
}: {
|
|
children: React.ReactNode;
|
|
style?: object;
|
|
}) {
|
|
const { colors } = useAppTheme();
|
|
|
|
return (
|
|
<Text style={[styles.heading, { color: colors.foreground }, style]}>{children}</Text>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
row: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
},
|
|
noShrink: {
|
|
flexShrink: 0,
|
|
},
|
|
markBox: {
|
|
flexShrink: 0,
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
},
|
|
markImage: {
|
|
width: "100%",
|
|
height: "100%",
|
|
},
|
|
heading: {
|
|
fontFamily: fonts.heading,
|
|
},
|
|
});
|