140 lines
4.1 KiB
TypeScript
140 lines
4.1 KiB
TypeScript
|
|
/**
|
|||
|
|
* 频道选择(瞬间 / 长文共用)
|
|||
|
|
* 包含「发表至」入口行 + 展开后的频道单选面板。
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import React from 'react';
|
|||
|
|
import { View, TouchableOpacity, StyleSheet } from 'react-native';
|
|||
|
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|||
|
|
import { Text } from '../../../components/common';
|
|||
|
|
import { spacing, fontSizes, borderRadius, useAppColors, type AppColors } from '../../../theme';
|
|||
|
|
|
|||
|
|
export type ChannelOption = { id: string; name: string };
|
|||
|
|
|
|||
|
|
interface ChannelPickerProps {
|
|||
|
|
channels: ChannelOption[];
|
|||
|
|
selectedId: string | null;
|
|||
|
|
onSelect: (channelId: string) => void;
|
|||
|
|
expanded: boolean;
|
|||
|
|
onToggle: () => void;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function ChannelPicker({
|
|||
|
|
channels,
|
|||
|
|
selectedId,
|
|||
|
|
onSelect,
|
|||
|
|
expanded,
|
|||
|
|
onToggle,
|
|||
|
|
}: ChannelPickerProps) {
|
|||
|
|
const colors = useAppColors();
|
|||
|
|
const styles = React.useMemo(() => createChannelPickerStyles(colors), [colors]);
|
|||
|
|
|
|||
|
|
const selectedName = React.useMemo(() => {
|
|||
|
|
if (!selectedId) return '';
|
|||
|
|
return channels.find(c => c.id === selectedId)?.name || '';
|
|||
|
|
}, [selectedId, channels]);
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<>
|
|||
|
|
{/* 入口行 */}
|
|||
|
|
<TouchableOpacity style={styles.channelEntryRow} onPress={onToggle} activeOpacity={0.8}>
|
|||
|
|
<Text variant="body" color={colors.text.secondary} style={styles.channelEntryLabel}>
|
|||
|
|
发表至:
|
|||
|
|
</Text>
|
|||
|
|
<View style={styles.channelEntryValueWrap}>
|
|||
|
|
<Text
|
|||
|
|
variant="body"
|
|||
|
|
color={selectedName ? colors.text.primary : colors.text.hint}
|
|||
|
|
style={styles.channelEntryValue}
|
|||
|
|
>
|
|||
|
|
{selectedName || '选择频道'}
|
|||
|
|
</Text>
|
|||
|
|
<MaterialCommunityIcons name="chevron-right" size={18} color={colors.text.hint} />
|
|||
|
|
</View>
|
|||
|
|
</TouchableOpacity>
|
|||
|
|
|
|||
|
|
{/* 展开面板 */}
|
|||
|
|
{expanded && (
|
|||
|
|
<View style={styles.tagsSection}>
|
|||
|
|
<View style={styles.tagsContainer}>
|
|||
|
|
{channels.map((channel) => {
|
|||
|
|
const isSelected = selectedId === channel.id;
|
|||
|
|
return (
|
|||
|
|
<TouchableOpacity
|
|||
|
|
key={channel.id}
|
|||
|
|
style={[styles.tag, isSelected && styles.channelSelected]}
|
|||
|
|
onPress={() => onSelect(channel.id)}
|
|||
|
|
>
|
|||
|
|
<Text
|
|||
|
|
variant="caption"
|
|||
|
|
color={isSelected ? colors.primary.contrast : colors.text.secondary}
|
|||
|
|
style={styles.tagText}
|
|||
|
|
>
|
|||
|
|
{channel.name}
|
|||
|
|
</Text>
|
|||
|
|
</TouchableOpacity>
|
|||
|
|
);
|
|||
|
|
})}
|
|||
|
|
</View>
|
|||
|
|
{channels.length === 0 && (
|
|||
|
|
<Text variant="caption" color={colors.text.hint}>暂无可用频道</Text>
|
|||
|
|
)}
|
|||
|
|
</View>
|
|||
|
|
)}
|
|||
|
|
</>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function createChannelPickerStyles(colors: AppColors) {
|
|||
|
|
return StyleSheet.create({
|
|||
|
|
channelEntryRow: {
|
|||
|
|
flexDirection: 'row',
|
|||
|
|
alignItems: 'center',
|
|||
|
|
justifyContent: 'space-between',
|
|||
|
|
paddingHorizontal: spacing.lg,
|
|||
|
|
paddingVertical: spacing.sm,
|
|||
|
|
borderTopWidth: StyleSheet.hairlineWidth,
|
|||
|
|
borderTopColor: colors.divider,
|
|||
|
|
backgroundColor: colors.background.paper,
|
|||
|
|
},
|
|||
|
|
channelEntryLabel: {
|
|||
|
|
fontSize: fontSizes.md,
|
|||
|
|
},
|
|||
|
|
channelEntryValueWrap: {
|
|||
|
|
flexDirection: 'row',
|
|||
|
|
alignItems: 'center',
|
|||
|
|
gap: spacing.xs,
|
|||
|
|
},
|
|||
|
|
channelEntryValue: {
|
|||
|
|
fontSize: fontSizes.md,
|
|||
|
|
},
|
|||
|
|
tagsSection: {
|
|||
|
|
paddingHorizontal: spacing.lg,
|
|||
|
|
paddingTop: spacing.md,
|
|||
|
|
paddingBottom: spacing.sm,
|
|||
|
|
borderTopWidth: StyleSheet.hairlineWidth,
|
|||
|
|
borderTopColor: colors.divider,
|
|||
|
|
backgroundColor: colors.background.paper,
|
|||
|
|
},
|
|||
|
|
tagsContainer: {
|
|||
|
|
flexDirection: 'row',
|
|||
|
|
flexWrap: 'wrap',
|
|||
|
|
gap: spacing.sm,
|
|||
|
|
},
|
|||
|
|
tag: {
|
|||
|
|
backgroundColor: colors.background.default,
|
|||
|
|
borderRadius: borderRadius.full,
|
|||
|
|
paddingHorizontal: spacing.md,
|
|||
|
|
paddingVertical: spacing.xs,
|
|||
|
|
borderWidth: 1,
|
|||
|
|
borderColor: colors.divider,
|
|||
|
|
},
|
|||
|
|
channelSelected: {
|
|||
|
|
backgroundColor: colors.primary.main,
|
|||
|
|
borderColor: colors.primary.main,
|
|||
|
|
},
|
|||
|
|
tagText: {
|
|||
|
|
fontWeight: '500',
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
}
|