Live video on the AT Protocol
1import { ImageStyle, StyleSheet, TextStyle, ViewStyle } from "react-native";
2
3// React Native style utilities
4type Style = ViewStyle | TextStyle | ImageStyle;
5
6/**
7 * Merges React Native styles similar to how cn() merges CSS classes
8 * Handles arrays, objects, and falsy values
9 */
10export function mergeStyles(
11 ...styles: (Style | Style[] | undefined | null | false)[]
12): Style {
13 const validStyles = styles.filter(Boolean).flat() as Style[];
14 return StyleSheet.flatten(validStyles) || {};
15}
16
17/**
18 * Creates a style merger function that includes base styles
19 * Useful for component variants
20 */
21export function createStyleMerger(baseStyle: Style) {
22 return (...styles: (Style | Style[] | undefined | null | false)[]) => {
23 return mergeStyles(baseStyle, ...styles);
24 };
25}
26
27/**
28 * Conditionally applies styles based on boolean conditions
29 */
30export function conditionalStyle(
31 condition: boolean,
32 trueStyle: Style,
33 falseStyle?: Style,
34): Style | undefined {
35 return condition ? trueStyle : falseStyle;
36}
37
38/**
39 * Creates responsive values based on screen dimensions
40 */
41export function responsiveValue<T>(
42 values: {
43 sm?: T;
44 md?: T;
45 lg?: T;
46 xl?: T;
47 default: T;
48 },
49 screenWidth: number,
50): T {
51 if (screenWidth >= 1280 && values.xl !== undefined) return values.xl;
52 if (screenWidth >= 1024 && values.lg !== undefined) return values.lg;
53 if (screenWidth >= 768 && values.md !== undefined) return values.md;
54 if (screenWidth >= 640 && values.sm !== undefined) return values.sm;
55 return values.default;
56}
57
58/**
59 * Creates platform-specific styles
60 */
61export function platformStyle(styles: {
62 ios?: Style;
63 android?: Style;
64 web?: Style;
65 default?: Style;
66}): Style {
67 const Platform = require("react-native").Platform;
68
69 if (Platform.OS === "ios" && styles.ios) return styles.ios;
70 if (Platform.OS === "android" && styles.android) return styles.android;
71 if (Platform.OS === "web" && styles.web) return styles.web;
72 return styles.default || {};
73}
74
75/**
76 * Converts hex color to rgba
77 */
78export function hexToRgba(hex: string, alpha: number = 1): string {
79 const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
80 if (!result) return hex;
81
82 const r = parseInt(result[1], 16);
83 const g = parseInt(result[2], 16);
84 const b = parseInt(result[3], 16);
85
86 return `rgba(${r}, ${g}, ${b}, ${alpha})`;
87}
88
89/**
90 * Creates a debounced function for performance
91 */
92export function debounce<T extends (...args: any[]) => any>(
93 func: T,
94 delay: number,
95): (...args: Parameters<T>) => void {
96 let timeoutId: NodeJS.Timeout;
97
98 return (...args: Parameters<T>) => {
99 clearTimeout(timeoutId);
100 timeoutId = setTimeout(() => func(...args), delay);
101 };
102}
103
104/**
105 * Creates a throttled function for performance
106 */
107export function throttle<T extends (...args: any[]) => any>(
108 func: T,
109 delay: number,
110): (...args: Parameters<T>) => void {
111 let lastCall = 0;
112
113 return (...args: Parameters<T>) => {
114 const now = Date.now();
115 if (now - lastCall >= delay) {
116 lastCall = now;
117 func(...args);
118 }
119 };
120}
121
122/**
123 * Type-safe component prop forwarding
124 */
125export function forwardProps<T extends Record<string, any>>(
126 props: T,
127 omit: (keyof T)[],
128): Omit<T, keyof T extends string ? keyof T : never> {
129 const result = { ...props };
130 omit.forEach((key) => delete result[key]);
131 return result;
132}