diff options
Diffstat (limited to 'src/view/com/util/Picker.tsx')
-rw-r--r-- | src/view/com/util/Picker.tsx | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/src/view/com/util/Picker.tsx b/src/view/com/util/Picker.tsx new file mode 100644 index 000000000..a02daef15 --- /dev/null +++ b/src/view/com/util/Picker.tsx @@ -0,0 +1,155 @@ +import React, {useRef} from 'react' +import { + StyleProp, + StyleSheet, + Text, + TextStyle, + TouchableOpacity, + TouchableWithoutFeedback, + View, + ViewStyle, +} from 'react-native' +import { + FontAwesomeIcon, + FontAwesomeIconStyle, +} from '@fortawesome/react-native-fontawesome' +import RootSiblings from 'react-native-root-siblings' +import {colors} from '../../lib/styles' + +interface PickerItem { + value: string + label: string +} + +interface PickerOpts { + style?: StyleProp<ViewStyle> + labelStyle?: StyleProp<TextStyle> + iconStyle?: FontAwesomeIconStyle + items: PickerItem[] + value: string + onChange: (value: string) => void + enabled?: boolean +} + +const MENU_WIDTH = 200 + +export function Picker({ + style, + labelStyle, + iconStyle, + items, + value, + onChange, + enabled, +}: PickerOpts) { + const ref = useRef<View>(null) + const valueLabel = items.find(item => item.value === value)?.label || value + const onPress = () => { + if (!enabled) { + return + } + ref.current?.measure( + ( + _x: number, + _y: number, + width: number, + height: number, + pageX: number, + pageY: number, + ) => { + createDropdownMenu(pageX, pageY + height, MENU_WIDTH, items, onChange) + }, + ) + } + return ( + <TouchableWithoutFeedback onPress={onPress}> + <View style={[styles.outer, style]} ref={ref}> + <View style={styles.label}> + <Text style={labelStyle}>{valueLabel}</Text> + </View> + <FontAwesomeIcon icon="angle-down" style={[styles.icon, iconStyle]} /> + </View> + </TouchableWithoutFeedback> + ) +} + +function createDropdownMenu( + x: number, + y: number, + width: number, + items: PickerItem[], + onChange: (value: string) => void, +): RootSiblings { + const onPressItem = (index: number) => { + sibling.destroy() + onChange(items[index].value) + } + const onOuterPress = () => sibling.destroy() + const sibling = new RootSiblings( + ( + <> + <TouchableWithoutFeedback onPress={onOuterPress}> + <View style={styles.bg} /> + </TouchableWithoutFeedback> + <View style={[styles.menu, {left: x, top: y, width}]}> + {items.map((item, index) => ( + <TouchableOpacity + key={index} + style={[styles.menuItem, index !== 0 && styles.menuItemBorder]} + onPress={() => onPressItem(index)}> + <Text style={styles.menuItemLabel}>{item.label}</Text> + </TouchableOpacity> + ))} + </View> + </> + ), + ) + return sibling +} + +const styles = StyleSheet.create({ + outer: { + flexDirection: 'row', + alignItems: 'center', + }, + label: { + marginRight: 5, + }, + icon: {}, + bg: { + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0, + backgroundColor: '#000', + opacity: 0.1, + }, + menu: { + position: 'absolute', + backgroundColor: '#fff', + borderRadius: 14, + opacity: 1, + paddingVertical: 6, + }, + menuItem: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: 6, + paddingLeft: 15, + paddingRight: 30, + }, + menuItemBorder: { + borderTopWidth: 1, + borderTopColor: colors.gray2, + marginTop: 4, + paddingTop: 12, + }, + menuItemIcon: { + marginLeft: 6, + marginRight: 8, + }, + menuItemLabel: { + fontSize: 15, + }, +}) |