mediasfu-reactnative-expo
Version:
mediasfu-reactnative-expo – Expo-managed React Native WebRTC SDK for video conferencing, webinars, live streaming, broadcast, screen sharing, whiteboard, chat, recording, live subtitles, translation, and AI agent rooms on iOS, Android, and web. Prebuilt r
231 lines • 8.66 kB
JavaScript
// MainAspectComponent.tsx
import React, { useState, useEffect } from 'react';
import { View, StyleSheet, Dimensions, } from 'react-native';
/**
* MainAspectComponent - Responsive container with screen size detection
*
* MainAspectComponent is a React Native component that dynamically adjusts its
* dimensions based on window size and provides real-time screen size state updates
* (wide/medium/small). It automatically recalculates dimensions on window resize
* and adjusts available height when control bars are visible.
*
* **Key Features:**
* - Responsive dimension calculation based on window size
* - Automatic screen size classification (wide >= 768px, medium 576-767px, small < 576px)
* - Height adjustment for control bar visibility
* - Real-time window resize/rotation handling
* - Screen size state callbacks for adaptive UI
* - Custom fractional sizing support
*
* **UI Customization:**
* This component can be replaced via `uiOverrides.mainAspectComponent` to
* provide a completely custom aspect-aware container.
*
* @component
* @param {MainAspectComponentOptions} props - Configuration options for the aspect container
*
* @returns {JSX.Element} Rendered responsive aspect container
*
* @example
* // Basic usage - Full-screen with control bar adjustment
* import React from 'react';
* import { MainAspectComponent } from 'mediasfu-reactnative-expo';
*
* function ResponsiveMeetingLayout() {
* const [isWide, setIsWide] = React.useState(false);
* const [isMedium, setIsMedium] = React.useState(false);
* const [isSmall, setIsSmall] = React.useState(false);
*
* return (
* <MainAspectComponent
* backgroundColor="#000000"
* showControls={true}
* containerWidthFraction={1}
* containerHeightFraction={1}
* defaultFraction={0.94}
* updateIsWideScreen={setIsWide}
* updateIsMediumScreen={setIsMedium}
* updateIsSmallScreen={setIsSmall}
* >
* {isWide && <WideScreenLayout />}
* {isMedium && <MediumScreenLayout />}
* {isSmall && <SmallScreenLayout />}
* </MainAspectComponent>
* );
* }
*
* @example
* // Custom fractions without control bar adjustment
* <MainAspectComponent
* backgroundColor="white"
* showControls={false}
* containerWidthFraction={0.85}
* containerHeightFraction={0.9}
* updateIsWideScreen={(isWide) => console.log('Wide:', isWide)}
* updateIsMediumScreen={(isMedium) => console.log('Medium:', isMedium)}
* updateIsSmallScreen={(isSmall) => console.log('Small:', isSmall)}
* >
* <MeetingContent />
* </MainAspectComponent>
*
* @example
* // With custom content renderer (add breakpoint indicator)
* <MainAspectComponent
* backgroundColor="#f0f0f0"
* showControls={true}
* updateIsWideScreen={setIsWide}
* updateIsMediumScreen={setIsMedium}
* updateIsSmallScreen={setIsSmall}
* renderContent={({ defaultContent, dimensions }) => (
* <>
* <View style={{ position: 'absolute', top: 5, right: 5, zIndex: 100 }}>
* <Text>
* {dimensions.width >= 768 ? 'Wide' : dimensions.width >= 576 ? 'Medium' : 'Small'}
* ({dimensions.width}x{dimensions.height})
* </Text>
* </View>
* {defaultContent}
* </>
* )}
* >
* <ResponsiveGrid />
* </MainAspectComponent>
*
* @example
* // Using uiOverrides for complete aspect container replacement
* import { MyCustomAspectContainer } from './MyCustomAspectContainer';
*
* const sessionConfig = {
* credentials: { apiKey: 'your-api-key' },
* uiOverrides: {
* mainAspectComponent: {
* component: MyCustomAspectContainer,
* injectedProps: {
* customBreakpoints: { wide: 1024, medium: 640 },
* },
* },
* },
* };
*
* // MyCustomAspectContainer.tsx
* export const MyCustomAspectContainer = (props: MainAspectComponentOptions & { customBreakpoints: { wide: number; medium: number } }) => {
* const [dimensions, setDimensions] = React.useState({ width: 0, height: 0 });
*
* React.useEffect(() => {
* const updateDimensions = () => {
* const { width, height } = Dimensions.get('window');
* setDimensions({ width, height });
* props.updateIsWideScreen(width >= props.customBreakpoints.wide);
* props.updateIsMediumScreen(width >= props.customBreakpoints.medium && width < props.customBreakpoints.wide);
* props.updateIsSmallScreen(width < props.customBreakpoints.medium);
* };
* const subscription = Dimensions.addEventListener('change', updateDimensions);
* updateDimensions();
* return () => subscription?.remove();
* }, []);
*
* return (
* <View style={{ width: dimensions.width, height: dimensions.height, backgroundColor: props.backgroundColor }}>
* {props.children}
* </View>
* );
* };
*/
const MainAspectComponent = ({ backgroundColor = 'transparent', children, showControls = true, containerWidthFraction = 1, containerHeightFraction = 1, defaultFraction = 0.94, updateIsWideScreen, updateIsMediumScreen, updateIsSmallScreen, style, renderContent, renderContainer, }) => {
const [aspectStyles, setAspectStyles] = useState({
height: showControls
? Math.floor(containerHeightFraction * Dimensions.get('window').height * defaultFraction)
: Math.floor(containerHeightFraction * Dimensions.get('window').height),
width: Math.floor(containerWidthFraction * Dimensions.get('window').width),
});
useEffect(() => {
const updateAspectStyles = ({ window }) => {
const windowHeight = window.height;
const windowWidth = window.width;
const parentWidth = Math.floor(containerWidthFraction * windowWidth);
const parentHeight = showControls
? Math.floor(containerHeightFraction * windowHeight * defaultFraction)
: Math.floor(containerHeightFraction * windowHeight);
let isWideScreen = parentWidth >= 768;
const isMediumScreen = parentWidth >= 576 && parentWidth < 768;
const isSmallScreen = parentWidth < 576;
if (!isWideScreen && parentWidth > 1.5 * parentHeight) {
isWideScreen = true;
}
updateIsWideScreen(isWideScreen);
updateIsMediumScreen(isMediumScreen);
updateIsSmallScreen(isSmallScreen);
setAspectStyles({
height: showControls
? Math.floor(containerHeightFraction * windowHeight * defaultFraction)
: Math.floor(containerHeightFraction * windowHeight),
width: Math.floor(containerWidthFraction * windowWidth),
});
};
// Initial setup
const { width, height } = Dimensions.get('window');
updateAspectStyles({
window: {
width, height, scale: 1, fontScale: 1,
},
screen: {
width, height, scale: 1, fontScale: 1,
},
});
// Subscribe to dimension changes
const subscription = Dimensions.addEventListener('change', updateAspectStyles);
return () => {
// Cleanup listener on component unmount
if (subscription?.remove) {
subscription.remove();
}
else {
// For older React Native versions
subscription.remove();
}
};
}, [
showControls,
containerHeightFraction,
containerWidthFraction,
defaultFraction,
updateIsWideScreen,
updateIsMediumScreen,
updateIsSmallScreen,
]);
const dimensions = {
width: aspectStyles.width,
height: aspectStyles.height,
};
const defaultContent = children;
const content = renderContent
? renderContent({ defaultContent, dimensions })
: defaultContent;
const defaultContainer = (<View style={[
styles.aspectContainer,
{
backgroundColor,
height: aspectStyles.height,
width: aspectStyles.width,
},
style,
]}>
{content}
</View>);
return renderContainer
? renderContainer({ defaultContainer, dimensions })
: defaultContainer;
};
export default MainAspectComponent;
/**
* Stylesheet for the MainAspectComponent.
*/
const styles = StyleSheet.create({
aspectContainer: {
flex: 1,
overflow: 'hidden',
margin: 0,
padding: 0,
},
});
//# sourceMappingURL=MainAspectComponent.js.map