|
| 1 | +import React from 'react'; |
| 2 | +import { TouchableOpacity } from 'react-native'; |
| 3 | +import { Ionicons } from '@expo/vector-icons'; |
| 4 | +import { ButtonPosition, ButtonLayoutDirection } from '../types'; |
| 5 | +import { ICON_SIZES, calculateButtonPosition } from '../constants'; |
| 6 | +import { getResponsiveStyles } from '../styles'; |
| 7 | +import { CustomButton } from './CustomButton'; |
| 8 | + |
| 9 | +interface AbsoluteButtonsProps { |
| 10 | + isControlsVisible: boolean; |
| 11 | + onBack?: () => void; |
| 12 | + backButtonPosition?: ButtonPosition; |
| 13 | + shareButtonPosition?: ButtonPosition; |
| 14 | + onShare: () => void; |
| 15 | + fullScreen?: any; |
| 16 | + isFullScreen: boolean; |
| 17 | + onToggleFullScreen: () => void; |
| 18 | + buttonGroups: any[]; |
| 19 | + isLandscape: boolean; |
| 20 | +} |
| 21 | + |
| 22 | +export function AbsoluteButtons({ |
| 23 | + isControlsVisible, |
| 24 | + onBack, |
| 25 | + backButtonPosition, |
| 26 | + shareButtonPosition, |
| 27 | + onShare, |
| 28 | + fullScreen, |
| 29 | + isFullScreen, |
| 30 | + onToggleFullScreen, |
| 31 | + buttonGroups, |
| 32 | + isLandscape |
| 33 | +}: AbsoluteButtonsProps) { |
| 34 | + if (!isControlsVisible) return null; |
| 35 | + |
| 36 | + const responsiveStyles = getResponsiveStyles(isLandscape); |
| 37 | + |
| 38 | + // Create default full screen button if enabled |
| 39 | + const defaultFullScreenButton = fullScreen?.enabled === true && fullScreen?.button ? { |
| 40 | + ...fullScreen.button, |
| 41 | + onPress: fullScreen.button.onPress || onToggleFullScreen |
| 42 | + } : fullScreen?.enabled === true ? { |
| 43 | + icon: isFullScreen ? 'contract-outline' : 'expand-outline', |
| 44 | + position: ButtonPosition.NE, |
| 45 | + onPress: onToggleFullScreen |
| 46 | + } : null; |
| 47 | + |
| 48 | + // Process button groups format |
| 49 | + const processedButtonGroups: Record<string, { buttons: any[], layoutDirection: ButtonLayoutDirection }> = {}; |
| 50 | + |
| 51 | + buttonGroups.forEach(group => { |
| 52 | + processedButtonGroups[group.position] = { |
| 53 | + buttons: group.buttons, |
| 54 | + layoutDirection: group.layoutDirection || ButtonLayoutDirection.VERTICAL |
| 55 | + }; |
| 56 | + }); |
| 57 | + |
| 58 | + // Add default full screen button if enabled and not already in a group |
| 59 | + if (defaultFullScreenButton && !processedButtonGroups[ButtonPosition.NE]) { |
| 60 | + processedButtonGroups[ButtonPosition.NE] = { |
| 61 | + buttons: [defaultFullScreenButton], |
| 62 | + layoutDirection: ButtonLayoutDirection.VERTICAL |
| 63 | + }; |
| 64 | + } else if (defaultFullScreenButton && processedButtonGroups[ButtonPosition.NE]) { |
| 65 | + // Check if full screen button is already in the group to avoid duplicates |
| 66 | + const existingButtons = processedButtonGroups[ButtonPosition.NE].buttons; |
| 67 | + const hasFullScreenButton = existingButtons.some(button => |
| 68 | + button.icon === defaultFullScreenButton.icon || |
| 69 | + (button.icon === 'expand-outline' || button.icon === 'contract-outline') |
| 70 | + ); |
| 71 | + |
| 72 | + if (!hasFullScreenButton) { |
| 73 | + processedButtonGroups[ButtonPosition.NE].buttons.push(defaultFullScreenButton); |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + // Filter for absolute positioning (not in top controls bar) |
| 78 | + const absolutePositions = [ButtonPosition.SE, ButtonPosition.SW, ButtonPosition.S, ButtonPosition.E, ButtonPosition.W]; |
| 79 | + const absoluteButtonGroups = Object.entries(processedButtonGroups).filter(([position]) => |
| 80 | + absolutePositions.includes(position as ButtonPosition) |
| 81 | + ); |
| 82 | + |
| 83 | + // Render buttons with enhanced spacing and layout direction |
| 84 | + const renderedButtons: React.ReactElement[] = []; |
| 85 | + |
| 86 | + absoluteButtonGroups.forEach(([position, { buttons, layoutDirection }]) => { |
| 87 | + buttons.forEach((button, index) => { |
| 88 | + // Get base position style |
| 89 | + const basePositionStyle = (() => { |
| 90 | + switch (button.position) { |
| 91 | + case ButtonPosition.SE: return responsiveStyles.buttonPositionSE; |
| 92 | + case ButtonPosition.SW: return responsiveStyles.buttonPositionSW; |
| 93 | + case ButtonPosition.S: return responsiveStyles.buttonPositionS; |
| 94 | + case ButtonPosition.E: return responsiveStyles.buttonPositionE; |
| 95 | + case ButtonPosition.W: return responsiveStyles.buttonPositionW; |
| 96 | + default: return {}; |
| 97 | + } |
| 98 | + })(); |
| 99 | + |
| 100 | + // Calculate spacing offset with layout direction support |
| 101 | + const spacingStyle = calculateButtonPosition( |
| 102 | + position, |
| 103 | + index, |
| 104 | + buttons.length, |
| 105 | + isLandscape, |
| 106 | + layoutDirection |
| 107 | + ); |
| 108 | + |
| 109 | + // Combine base position with spacing |
| 110 | + const finalStyle = { ...basePositionStyle, ...spacingStyle }; |
| 111 | + |
| 112 | + renderedButtons.push( |
| 113 | + <CustomButton |
| 114 | + key={`absolute-${position}-${index}`} |
| 115 | + config={button} |
| 116 | + isLandscape={isLandscape} |
| 117 | + style={finalStyle} |
| 118 | + defaultOnPress={button === defaultFullScreenButton ? onToggleFullScreen : undefined} |
| 119 | + /> |
| 120 | + ); |
| 121 | + }); |
| 122 | + }); |
| 123 | + |
| 124 | + return ( |
| 125 | + <> |
| 126 | + {onBack && backButtonPosition === ButtonPosition.SE && ( |
| 127 | + <TouchableOpacity |
| 128 | + style={[responsiveStyles.topButton, responsiveStyles.buttonPositionSE]} |
| 129 | + onPress={onBack} |
| 130 | + > |
| 131 | + <Ionicons name="close" size={ICON_SIZES.top} color="white" /> |
| 132 | + </TouchableOpacity> |
| 133 | + )} |
| 134 | + {shareButtonPosition === ButtonPosition.SE && ( |
| 135 | + <TouchableOpacity |
| 136 | + style={[responsiveStyles.topButton, responsiveStyles.buttonPositionSE]} |
| 137 | + onPress={onShare} |
| 138 | + > |
| 139 | + <Ionicons name="share-outline" size={ICON_SIZES.top} color="white" /> |
| 140 | + </TouchableOpacity> |
| 141 | + )} |
| 142 | + {renderedButtons} |
| 143 | + </> |
| 144 | + ); |
| 145 | +} |
0 commit comments