104 lines
3.0 KiB
TypeScript
104 lines
3.0 KiB
TypeScript
import { Dimensions, PixelRatio, Platform } from 'react-native';
|
|
|
|
// Base dimensions (design reference - typically iPhone 14/15 or similar)
|
|
const BASE_WIDTH = 390;
|
|
const BASE_HEIGHT = 844;
|
|
|
|
const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
|
|
|
|
/**
|
|
* Scale a value based on screen width (horizontal scaling)
|
|
* Use for: horizontal padding, margins, widths, font sizes
|
|
*/
|
|
export function scale(size: number): number {
|
|
const scaleFactor = SCREEN_WIDTH / BASE_WIDTH;
|
|
const newSize = size * scaleFactor;
|
|
|
|
if (Platform.OS === 'web') {
|
|
// On web, respect user zoom settings by using rem-like scaling
|
|
return size;
|
|
}
|
|
|
|
return Math.round(PixelRatio.roundToNearestPixel(newSize));
|
|
}
|
|
|
|
/**
|
|
* Scale a value based on screen height (vertical scaling)
|
|
* Use for: vertical padding, margins, heights
|
|
*/
|
|
export function verticalScale(size: number): number {
|
|
const scaleFactor = SCREEN_HEIGHT / BASE_HEIGHT;
|
|
const newSize = size * scaleFactor;
|
|
|
|
if (Platform.OS === 'web') {
|
|
return size;
|
|
}
|
|
|
|
return Math.round(PixelRatio.roundToNearestPixel(newSize));
|
|
}
|
|
|
|
/**
|
|
* Moderate scaling - scales less aggressively (recommended for most use cases)
|
|
* Use for: font sizes, icon sizes, border radius
|
|
* @param factor - how much to scale (0 = no scaling, 1 = full scaling, default 0.5)
|
|
*/
|
|
export function moderateScale(size: number, factor: number = 0.5): number {
|
|
const scaleFactor = SCREEN_WIDTH / BASE_WIDTH;
|
|
const newSize = size + (scaleFactor - 1) * size * factor;
|
|
|
|
if (Platform.OS === 'web') {
|
|
return size;
|
|
}
|
|
|
|
return Math.round(PixelRatio.roundToNearestPixel(newSize));
|
|
}
|
|
|
|
/**
|
|
* Get responsive value based on screen size breakpoints
|
|
* Use for: completely different layouts at different sizes
|
|
*/
|
|
export function responsive<T>(options: {
|
|
small?: T; // < 375px (iPhone SE, small phones)
|
|
medium?: T; // 375-428px (most phones)
|
|
large?: T; // > 428px (tablets, large phones, web)
|
|
default: T;
|
|
}): T {
|
|
if (SCREEN_WIDTH < 375) {
|
|
return options.small ?? options.default;
|
|
}
|
|
if (SCREEN_WIDTH <= 428) {
|
|
return options.medium ?? options.default;
|
|
}
|
|
return options.large ?? options.default;
|
|
}
|
|
|
|
/**
|
|
* Check if device is a tablet or large screen
|
|
*/
|
|
export function isTablet(): boolean {
|
|
const aspectRatio = SCREEN_HEIGHT / SCREEN_WIDTH;
|
|
return (
|
|
(Platform.OS !== 'web' && SCREEN_WIDTH >= 768) ||
|
|
(Platform.OS === 'web' && SCREEN_WIDTH >= 1024) ||
|
|
aspectRatio < 1.6
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get screen dimensions (updates on rotation/resize)
|
|
*/
|
|
export function getScreenDimensions() {
|
|
const { width, height } = Dimensions.get('window');
|
|
return { width, height };
|
|
}
|
|
|
|
// Font scaling with accessibility support
|
|
export function scaledFontSize(size: number): number {
|
|
const scaled = moderateScale(size, 0.3);
|
|
// Respect system font scaling but cap it to prevent extreme sizes
|
|
const maxScale = 1.3;
|
|
const fontScale = Math.min(PixelRatio.getFontScale(), maxScale);
|
|
return Math.round(scaled * fontScale);
|
|
}
|
|
|