mirror of
https://github.com/zimplexing/OrionTV.git
synced 2026-02-04 03:36:29 +08:00
feat: Enhance mobile and tablet support with responsive layout adjustments and new navigation components
This commit is contained in:
129
hooks/useResponsiveLayout.ts
Normal file
129
hooks/useResponsiveLayout.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Dimensions, Platform } from 'react-native';
|
||||
|
||||
export type DeviceType = 'mobile' | 'tablet' | 'tv';
|
||||
|
||||
export interface ResponsiveConfig {
|
||||
deviceType: DeviceType;
|
||||
columns: number;
|
||||
cardWidth: number;
|
||||
cardHeight: number;
|
||||
spacing: number;
|
||||
isPortrait: boolean;
|
||||
screenWidth: number;
|
||||
screenHeight: number;
|
||||
}
|
||||
|
||||
const BREAKPOINTS = {
|
||||
mobile: { min: 0, max: 767 },
|
||||
tablet: { min: 768, max: 1023 },
|
||||
tv: { min: 1024, max: Infinity }
|
||||
};
|
||||
|
||||
const getDeviceType = (width: number): DeviceType => {
|
||||
const isTV = process.env.EXPO_TV === '1' || Platform.isTV;
|
||||
if (isTV) return 'tv';
|
||||
|
||||
if (width >= BREAKPOINTS.tv.min) return 'tv';
|
||||
if (width >= BREAKPOINTS.tablet.min) return 'tablet';
|
||||
return 'mobile';
|
||||
};
|
||||
|
||||
const getLayoutConfig = (deviceType: DeviceType, width: number, height: number, isPortrait: boolean): ResponsiveConfig => {
|
||||
const spacing = deviceType === 'mobile' ? 8 : deviceType === 'tablet' ? 12 : 16;
|
||||
|
||||
let columns: number;
|
||||
let cardWidth: number;
|
||||
let cardHeight: number;
|
||||
|
||||
switch (deviceType) {
|
||||
case 'mobile':
|
||||
columns = isPortrait ? 2 : 3;
|
||||
cardWidth = (width - spacing * (columns + 1)) / columns;
|
||||
cardHeight = cardWidth * 1.5; // 2:3 aspect ratio
|
||||
break;
|
||||
|
||||
case 'tablet':
|
||||
columns = isPortrait ? 3 : 4;
|
||||
cardWidth = (width - spacing * (columns + 1)) / columns;
|
||||
cardHeight = cardWidth * 1.4; // slightly less tall ratio
|
||||
break;
|
||||
|
||||
case 'tv':
|
||||
default:
|
||||
columns = 5;
|
||||
cardWidth = 160; // Fixed width for TV
|
||||
cardHeight = 240; // Fixed height for TV
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
deviceType,
|
||||
columns,
|
||||
cardWidth,
|
||||
cardHeight,
|
||||
spacing,
|
||||
isPortrait,
|
||||
screenWidth: width,
|
||||
screenHeight: height,
|
||||
};
|
||||
};
|
||||
|
||||
export const useResponsiveLayout = (): ResponsiveConfig => {
|
||||
const [dimensions, setDimensions] = useState(() => {
|
||||
const { width, height } = Dimensions.get('window');
|
||||
return { width, height };
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const subscription = Dimensions.addEventListener('change', ({ window }) => {
|
||||
setDimensions({ width: window.width, height: window.height });
|
||||
});
|
||||
|
||||
return () => subscription?.remove();
|
||||
}, []);
|
||||
|
||||
const { width, height } = dimensions;
|
||||
const isPortrait = height > width;
|
||||
const deviceType = getDeviceType(width);
|
||||
|
||||
return getLayoutConfig(deviceType, width, height, isPortrait);
|
||||
};
|
||||
|
||||
// Utility hook for responsive values
|
||||
export const useResponsiveValue = <T>(values: { mobile: T; tablet: T; tv: T }): T => {
|
||||
const { deviceType } = useResponsiveLayout();
|
||||
return values[deviceType];
|
||||
};
|
||||
|
||||
// Utility hook for responsive styles
|
||||
export const useResponsiveStyles = () => {
|
||||
const config = useResponsiveLayout();
|
||||
|
||||
return {
|
||||
// Common responsive styles
|
||||
container: {
|
||||
paddingHorizontal: config.spacing,
|
||||
},
|
||||
|
||||
// Card styles
|
||||
cardContainer: {
|
||||
width: config.cardWidth,
|
||||
height: config.cardHeight,
|
||||
marginBottom: config.spacing,
|
||||
},
|
||||
|
||||
// Grid styles
|
||||
gridContainer: {
|
||||
paddingHorizontal: config.spacing / 2,
|
||||
},
|
||||
|
||||
// Typography
|
||||
titleFontSize: config.deviceType === 'mobile' ? 18 : config.deviceType === 'tablet' ? 22 : 28,
|
||||
bodyFontSize: config.deviceType === 'mobile' ? 14 : config.deviceType === 'tablet' ? 16 : 18,
|
||||
|
||||
// Spacing
|
||||
sectionSpacing: config.deviceType === 'mobile' ? 16 : config.deviceType === 'tablet' ? 20 : 24,
|
||||
itemSpacing: config.spacing,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user