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
259 lines • 8.84 kB
JavaScript
// ControlButtonsComponent.tsx
import React from 'react';
import { View, Text, StyleSheet, Pressable, } from 'react-native';
import { FontAwesome5 } from "@expo/vector-icons";
/**
* ControlButtonsComponent - Flexible control button bar for meeting actions
*
* ControlButtonsComponent is a React Native component that renders a customizable
* row or column of control buttons (mute, video, screen share, etc.). Each button
* supports active/inactive states, custom icons, disabled states, and press feedback.
*
* **Key Features:**
* - Horizontal or vertical button arrangement
* - Active/inactive state visual feedback
* - Custom icon support (FontAwesome5 or custom components)
* - Pressed state animations
* - Disabled button handling
* - Flexible alignment options
* - Show/hide individual buttons
*
* **UI Customization:**
* This component can be replaced via `uiOverrides.controlButtonsComponent` to
* provide a completely custom control button bar.
*
* @component
* @param {ControlButtonsComponentOptions} props - Configuration options for the control buttons
*
* @returns {JSX.Element} Rendered control button bar
*
* @example
* // Basic usage - Horizontal button bar
* import React from 'react';
* import { ControlButtonsComponent, Button } from 'mediasfu-reactnative-expo';
*
* function MeetingControls() {
* const [isMuted, setIsMuted] = React.useState(false);
* const [isVideoOff, setIsVideoOff] = React.useState(false);
*
* const buttons: Button[] = [
* {
* name: 'Mic',
* icon: 'microphone',
* alternateIcon: 'microphone-slash',
* active: isMuted,
* onPress: () => setIsMuted(!isMuted),
* activeColor: '#FF0000',
* inActiveColor: '#FFFFFF',
* },
* {
* name: 'Video',
* icon: 'video',
* alternateIcon: 'video-slash',
* active: isVideoOff,
* onPress: () => setIsVideoOff(!isVideoOff),
* activeColor: '#FF0000',
* inActiveColor: '#FFFFFF',
* },
* {
* name: 'Leave',
* icon: 'phone',
* onPress: () => console.log('Leave meeting'),
* backgroundColor: { default: '#FF0000', pressed: '#CC0000' },
* color: '#FFFFFF',
* },
* ];
*
* return (
* <ControlButtonsComponent
* buttons={buttons}
* alignment="center"
* buttonBackgroundColor={{ default: '#333333', pressed: '#555555' }}
* vertical={false}
* />
* );
* }
*
* @example
* // Vertical button bar with custom styling
* <ControlButtonsComponent
* buttons={controlButtons}
* alignment="flex-start"
* vertical={true}
* buttonsContainerStyle={{
* gap: 10,
* padding: 8,
* backgroundColor: 'rgba(0,0,0,0.7)',
* borderRadius: 8,
* }}
* />
*
* @example
* // With custom icon components
* const customButtons: Button[] = [
* {
* name: 'Custom',
* customComponent: <MyCustomButtonComponent />,
* onPress: () => console.log('Custom pressed'),
* },
* {
* name: 'Icon',
* iconComponent: <CustomIcon name="custom" />,
* alternateIconComponent: <CustomIcon name="custom-alt" />,
* active: isActive,
* onPress: toggleActive,
* },
* ];
*
* <ControlButtonsComponent
* buttons={customButtons}
* alignment="space-evenly"
* />
*
* @example
* // Using uiOverrides for complete control bar replacement
* import { MyCustomControlButtons } from './MyCustomControlButtons';
*
* const sessionConfig = {
* credentials: { apiKey: 'your-api-key' },
* uiOverrides: {
* controlButtonsComponent: {
* component: MyCustomControlButtons,
* injectedProps: {
* theme: 'modern',
* showLabels: true,
* },
* },
* },
* };
*
* // MyCustomControlButtons.tsx
* export const MyCustomControlButtons = (props: ControlButtonsComponentOptions & { theme: string; showLabels: boolean }) => {
* return (
* <View style={{ flexDirection: props.vertical ? 'column' : 'row', gap: 12 }}>
* {props.buttons.filter(btn => btn.show !== false).map((button, index) => (
* <Pressable
* key={index}
* onPress={button.onPress}
* disabled={button.disabled}
* style={{
* padding: 12,
* borderRadius: props.theme === 'modern' ? 24 : 8,
* backgroundColor: button.active ? '#007bff' : '#e0e0e0',
* }}
* >
* {button.customComponent || (
* <FontAwesome5
* name={button.active ? button.alternateIcon : button.icon}
* size={20}
* color={button.active ? button.activeColor : button.inActiveColor}
* />
* )}
* {props.showLabels && <Text>{button.name}</Text>}
* </Pressable>
* ))}
* </View>
* );
* };
*/
const ControlButtonsComponent = ({ buttons, buttonBackgroundColor, alignment = 'flex-start', vertical = false, buttonsContainerStyle, style, renderContent, renderContainer, }) => {
/**
* getAlignmentStyle - Computes alignment styles based on alignment prop.
* @returns {StyleProp<ViewStyle>} The computed alignment styles.
*/
const getAlignmentStyle = () => {
const alignmentStyle = {};
switch (alignment) {
case 'center':
alignmentStyle.justifyContent = 'center';
break;
case 'flex-end':
alignmentStyle.justifyContent = 'flex-end';
break;
case 'space-between':
alignmentStyle.justifyContent = 'space-between';
break;
case 'space-around':
alignmentStyle.justifyContent = 'space-around';
break;
case 'space-evenly':
alignmentStyle.justifyContent = 'space-evenly';
break;
case 'flex-start':
default:
alignmentStyle.justifyContent = 'flex-start';
break;
}
return alignmentStyle;
};
const dimensions = { width: 0, height: 0 }; // Dynamic sizing based on button count
const defaultContent = buttons.filter((button) => button.show !== false).map((button, index) => {
const iconNode = button.icon ? (button.active ? (button.alternateIconComponent ? (button.alternateIconComponent) : button.alternateIcon ? (<FontAwesome5 name={button.alternateIcon} size={24} color={button.activeColor || '#ffffff'}/>) : null) : button.iconComponent ? (button.iconComponent) : button.icon ? (<FontAwesome5 name={button.icon} size={24} color={button.inActiveColor || '#ffffff'}/>) : null) : (button.customComponent);
return (<Pressable key={index} style={({ pressed }) => [
styles.buttonContainer,
{
backgroundColor: pressed
? buttonBackgroundColor?.pressed || '#444'
: buttonBackgroundColor?.default || 'transparent',
},
vertical && styles.verticalButton,
]} onPress={button.onPress} disabled={button.disabled} accessibilityRole="button" accessibilityLabel={button.accessibilityLabel || button.name}>
<View style={styles.buttonContent}>
{iconNode}
{button.name && (<Text numberOfLines={1} style={[styles.buttonText, { color: button.color || '#ffffff' }]}>
{button.name}
</Text>)}
</View>
</Pressable>);
});
const content = renderContent
? renderContent({ defaultContent, dimensions })
: defaultContent;
const defaultContainer = (<View style={[
styles.container,
getAlignmentStyle(),
buttonsContainerStyle,
{ display: 'flex' },
style,
]}>
{content}
</View>);
return renderContainer
? renderContainer({ defaultContainer, dimensions })
: defaultContainer;
};
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
marginVertical: 6,
width: '100%',
maxWidth: '100%',
alignItems: 'center',
},
buttonContainer: {
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 6,
paddingHorizontal: 8,
borderRadius: 5,
marginHorizontal: 2,
minWidth: 32,
flexShrink: 0,
},
buttonContent: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
maxWidth: '100%',
},
verticalButton: {
flexDirection: 'column',
},
buttonText: {
fontSize: 12,
marginLeft: 6,
lineHeight: 14,
},
});
export default ControlButtonsComponent;
//# sourceMappingURL=ControlButtonsComponent.js.map