mirror of https://github.com/kortix-ai/suna.git
152 lines
5.4 KiB
TypeScript
152 lines
5.4 KiB
TypeScript
import { useTheme } from '@/hooks/useThemeColor';
|
|
import { UploadedFile } from '@/utils/file-upload';
|
|
import { X } from 'lucide-react-native';
|
|
import React from 'react';
|
|
import { ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native';
|
|
import { FileAttachment } from './FileAttachment';
|
|
|
|
interface AttachmentGroupProps {
|
|
attachments: string[] | UploadedFile[];
|
|
sandboxId?: string;
|
|
onFilePress?: (path: string) => void;
|
|
onRemove?: (index: number) => void;
|
|
layout?: 'inline' | 'grid';
|
|
showPreviews?: boolean;
|
|
maxHeight?: number;
|
|
}
|
|
|
|
export const AttachmentGroup: React.FC<AttachmentGroupProps> = ({
|
|
attachments,
|
|
sandboxId,
|
|
onFilePress,
|
|
onRemove,
|
|
layout = 'grid',
|
|
showPreviews = true,
|
|
maxHeight = 200
|
|
}) => {
|
|
const theme = useTheme();
|
|
|
|
// Create styles inside component to access theme
|
|
const styles = StyleSheet.create({
|
|
inlineContainer: {
|
|
|
|
},
|
|
inlineContent: {
|
|
paddingRight: 12,
|
|
paddingVertical: 8,
|
|
gap: 8,
|
|
},
|
|
inlineItem: {
|
|
marginRight: 8,
|
|
},
|
|
fileWrapper: {
|
|
position: 'relative',
|
|
paddingRight: 8,
|
|
marginRight: -8,
|
|
},
|
|
removeButton: {
|
|
position: 'absolute',
|
|
top: -5,
|
|
right: 0,
|
|
width: 22,
|
|
height: 22,
|
|
borderRadius: 11,
|
|
backgroundColor: theme.sidebar,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
zIndex: 10,
|
|
},
|
|
removeButtonInner: {
|
|
width: 16,
|
|
height: 16,
|
|
borderRadius: 8,
|
|
backgroundColor: theme.foreground,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
gridContainer: {
|
|
gap: 8,
|
|
marginTop: 8,
|
|
},
|
|
gridItem: {
|
|
width: '100%',
|
|
},
|
|
});
|
|
|
|
if (!attachments || attachments.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
const isInline = layout === 'inline';
|
|
|
|
if (isInline) {
|
|
return (
|
|
<ScrollView
|
|
horizontal
|
|
showsHorizontalScrollIndicator={false}
|
|
style={[styles.inlineContainer, { maxHeight }]}
|
|
contentContainerStyle={styles.inlineContent}
|
|
>
|
|
{attachments.map((attachment, index) => {
|
|
const isUploadedFile = typeof attachment === 'object';
|
|
const filepath = isUploadedFile ? attachment.path : attachment;
|
|
|
|
return (
|
|
<View key={index} style={styles.inlineItem}>
|
|
<View style={styles.fileWrapper}>
|
|
<FileAttachment
|
|
filepath={filepath}
|
|
sandboxId={sandboxId}
|
|
onPress={onFilePress}
|
|
layout="inline"
|
|
showPreview={showPreviews}
|
|
isUploading={isUploadedFile ? attachment.isUploading : false}
|
|
uploadError={isUploadedFile ? attachment.uploadError : undefined}
|
|
uploadedBlob={isUploadedFile ? attachment.cachedBlob : undefined}
|
|
localUri={isUploadedFile ? attachment.localUri : undefined}
|
|
/>
|
|
{onRemove && (
|
|
<TouchableOpacity
|
|
style={styles.removeButton}
|
|
onPress={() => onRemove(index)}
|
|
hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
|
|
>
|
|
<View style={styles.removeButtonInner}>
|
|
<X size={12} color={theme.background} strokeWidth={3} />
|
|
</View>
|
|
</TouchableOpacity>
|
|
)}
|
|
</View>
|
|
</View>
|
|
);
|
|
})}
|
|
</ScrollView>
|
|
);
|
|
}
|
|
|
|
// Grid layout
|
|
return (
|
|
<View style={styles.gridContainer}>
|
|
{attachments.map((attachment, index) => {
|
|
const isUploadedFile = typeof attachment === 'object';
|
|
const filepath = isUploadedFile ? attachment.path : attachment;
|
|
|
|
return (
|
|
<View key={index} style={styles.gridItem}>
|
|
<FileAttachment
|
|
filepath={filepath}
|
|
sandboxId={sandboxId}
|
|
onPress={onFilePress}
|
|
layout="grid"
|
|
showPreview={showPreviews}
|
|
isUploading={isUploadedFile ? attachment.isUploading : false}
|
|
uploadError={isUploadedFile ? attachment.uploadError : undefined}
|
|
uploadedBlob={isUploadedFile ? attachment.cachedBlob : undefined}
|
|
localUri={isUploadedFile ? attachment.localUri : undefined}
|
|
/>
|
|
</View>
|
|
);
|
|
})}
|
|
</View>
|
|
);
|
|
};
|