hoaphantn7604 / react-native-element-dropdown

A react-native dropdown component easy to customize for both iOS and Android.
MIT License
1k stars 173 forks source link

Not scrolling when used inside View in RN 0.71.2 #155

Open Bileeta123 opened 1 year ago

Bileeta123 commented 1 year ago

This is my view hierarchy looks like,

      `<View style={{flexDirection: 'row', flex: 1.2}}>

                <View style={{ flexDirection: 'row', flex: 1, alignItems: 'center', justifyContent: 'center' }}>

                    <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', flex: 0.8, }}>
                        <Text>.....</Text>
                        <TouchableOpacity>
                            <TextInput />
                        </TouchableOpacity>
                    </View>

                    <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', flex: 0.9, }}>
                        <Text>T....</Text>
                        <Dropdown
                            style={{
                                width: 90, borderWidth: 0.5, borderWidth: 1,
                                borderColor: '#0C453A',
                                borderRadius: 10,
                                minHeight: 40,
                                backgroundColor: 'white',
                            }}
                            placeholderStyle={{}}
                            selectedTextStyle={{}}
                            inputSearchStyle={{}}
                            iconStyle={{}}
                            data={tokenArr}
                            search
                            maxHeight={300}
                            minHeight={100}
                            labelField="TokenIdS"
                            valueField="Id"
                            searchField="TokenIdS"
                            placeholder={''}
                            searchPlaceholder="Search..."
                            value={selectedToken?.Id}
                            onFocus={() => setIsFocus(true)}
                            onBlur={() => setIsFocus(false)}
                            onChange={(item) => {
                                //setValue(item.value);
                                setSelectedToken(item)
                                //setIsFocus(false);
                            }}
                        />
                        <TouchableOpacity>
                            <Icon4/>
                        </TouchableOpacity>
                    </View>
                </View>

                <View style={{ flexDirection: 'row', flex: 0.91, alignItems: 'center'}}>

                    <View>
                        <View>
                            <Icon1/>
                            <Text>.....</Text>
                            <Text>.....</Text>
                        </View>
                     <View>
                     </View>
                        <View>
                            <Icon2/>
                            <Text>.....</Text>
                            <Text>.....</Text>
                        </View>
                    </View>
                    <View>
                        <Button>
                            ......
                        </Button>
                    </View>

                </View>

            </View>`

Drop down opens but cannot scroll it. But if i move the dropdown top of the hierarchy, as the first ui component of the parent view, it's working. Anyone know a fix?

"react-native": "0.71.2" "react-native-element-dropdown": "^2.8.0" "react-native-gesture-handler": "^2.9.0"

Nunu27 commented 1 year ago

i encounter the same issue. in my case it seems that the flexDirection: 'row' is causing the issue, when i remove it, the dropdown can scroll normally. still don't know what's the real problem yet

Bileeta123 commented 1 year ago

i encounter the same issue. in my case it seems that the flexDirection: 'row' is causing the issue, when i remove it, the dropdown can scroll normally. still don't know what's the real problem yet

Yes i also noticed that. It's working without flexbox. I dont no whether this is an RN issue or lib issue.

heinhtetaungg commented 1 year ago

So, how did you guys solve that issue? Just remove flexDirection:'row' ?

Raller commented 1 year ago

Anyone found a solution for this? I need to display the dropdown next to a TextInput, so I can't just remove flexDirection: 'row'

hoaphantn7604 commented 1 year ago

hi @Bileeta123 ,

                    <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', flex: 0.9, }}>
                        <Text>T....</Text>
                        <View style={{ flex:1 }}> // Add this line
                          <Dropdown
                            style={{
                                width: 90, borderWidth: 0.5, borderWidth: 1,
                                borderColor: '#0C453A',
                                borderRadius: 10,
                                minHeight: 40,
                                backgroundColor: 'white',
                            }}
                            placeholderStyle={{}}
                            selectedTextStyle={{}}
                            inputSearchStyle={{}}
                            iconStyle={{}}
                            data={tokenArr}
                            search
                            maxHeight={300}
                            minHeight={100}
                            labelField="TokenIdS"
                            valueField="Id"
                            searchField="TokenIdS"
                            placeholder={''}
                            searchPlaceholder="Search..."
                            value={selectedToken?.Id}
                            onFocus={() => setIsFocus(true)}
                            onBlur={() => setIsFocus(false)}
                            onChange={(item) => {
                                //setValue(item.value);
                                setSelectedToken(item)
                                //setIsFocus(false);
                            }}
                          />
                       </View> // Add this line
                        <TouchableOpacity>
                            <Icon4/>
                        </TouchableOpacity>
                    </View>

You try wrapping outside dropdown can fix this problem?

heinhtetaungg commented 1 year ago

hi @Bileeta123 ,

                    <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', flex: 0.9, }}>
                        <Text>T....</Text>
                        <View style={{ flex:1 }}> // Add this line
                          <Dropdown
                            style={{
                                width: 90, borderWidth: 0.5, borderWidth: 1,
                                borderColor: '#0C453A',
                                borderRadius: 10,
                                minHeight: 40,
                                backgroundColor: 'white',
                            }}
                            placeholderStyle={{}}
                            selectedTextStyle={{}}
                            inputSearchStyle={{}}
                            iconStyle={{}}
                            data={tokenArr}
                            search
                            maxHeight={300}
                            minHeight={100}
                            labelField="TokenIdS"
                            valueField="Id"
                            searchField="TokenIdS"
                            placeholder={''}
                            searchPlaceholder="Search..."
                            value={selectedToken?.Id}
                            onFocus={() => setIsFocus(true)}
                            onBlur={() => setIsFocus(false)}
                            onChange={(item) => {
                                //setValue(item.value);
                                setSelectedToken(item)
                                //setIsFocus(false);
                            }}
                          />
                       </View> // Add this line
                        <TouchableOpacity>
                            <Icon4/>
                        </TouchableOpacity>
                    </View>

You try wrapping outside dropdown can fix this problem?

It still can't scroll at right side of the screen . May be it is react native's issue ,but in ios it work.

Raller commented 1 year ago

If you inspect the dropdown when it is expanded, it looks like the scrollable view is placed to the left of the screen. image

It is also a problem with other dropdown libaries, so maybe it is a React Native issue?

heinhtetaungg commented 1 year ago

Guysss,any solution for this?

Nunu27 commented 1 year ago

I end up making my own dropdown component

HERYORDEJY commented 1 year ago

@Nunu27

Can you please share your own custom-built component? I have been stuck with this dropdown component issues

Nunu27 commented 1 year ago

it's just a basic implementation of the dropdown, but here you go.

import { FlatList, Modal, StyleSheet, View } from 'react-native';
import React, { memo, useCallback, useMemo, useRef, useState } from 'react';
import { RectButton } from 'react-native-gesture-handler';
import { Text } from './Themed';
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../constants';
import { Pressable } from 'react-native';

const ITEM_HEIGHT = 35;

const PickerItem = memo(
    ({
        item,
        labelStyle,
        style,
        activeStyle,
        activeLabelStyle,
        selected,
        onChange
    }) => (
        <Pressable
            android_ripple={{ color: Colors.ripple }}
            style={[
                styles.item,
                style,
                selected ? activeStyle || styles.itemActive : {}
            ]}
            onPress={() => {
                onChange(item);
            }}
        >
            <Text
                style={[
                    styles.itemLabel,
                    labelStyle,
                    selected ? activeLabelStyle || styles.itemLabelActive : {}
                ]}
                numberOfLines={1}
            >
                {item.label}
            </Text>
        </Pressable>
    ),
    ({ item: p, selected: s1 }, { item: n, selected: s2 }) =>
        p.label === n.label && s1 === s2
);

const Picker = ({
    items,
    value,
    setValue,
    setLabel,
    style,
    iconStyle,
    itemStyle,
    labelStyle,
    itemLabelStyle,
    activeItemStyle,
    activeLabelStyle,
    offset = 5
}) => {
    const view = useRef(null);
    const list = useRef(null);
    const [layout, setLayout] = useState(null);
    const [visible, setVisible] = useState(false);
    const current = useMemo(() => {
        const index = items.findIndex(({ value: v }) => v === value);

        return {
            index,
            label: items[index].label
        };
    }, [value]);

    const openModal = () => {
        setVisible(true);
        onLayout();
    };
    const closeModal = () => {
        setVisible(false);
    };

    const onValueChange = useCallback(({ label, value }) => {
        setLabel(label);
        setValue(value);
        closeModal();
    }, []);
    const renderItem = ({ item }) => (
        <PickerItem
            item={item}
            style={itemStyle}
            labelStyle={itemLabelStyle}
            activeStyle={activeItemStyle}
            activeLabelStyle={activeLabelStyle}
            selected={value === item.value}
            onChange={onValueChange}
        />
    );
    const keyExtractor = ({ label }) => label;
    const onLayout = () => {
        view.current?.measure((fx, fy, width, height, x, y) => {
            if (!x || layout?.trusty) return;

            setLayout({
                trusty: !!layout,
                height,
                width,
                x,
                y: height + y + offset - 10
            });
        });
    };
    const getItemLayout = (_, index) => ({
        length: ITEM_HEIGHT,
        offset: ITEM_HEIGHT * index,
        index
    });
    const layoutStyle = useMemo(
        () => ({
            position: 'absolute',
            top: layout?.y ?? 0,
            left: layout?.x ?? 0,
            width: layout?.width,
            maxHeight: 300
        }),
        [layout]
    );

    return (
        <View ref={view} onLayout={onLayout}>
            <RectButton
                style={[styles.button, style]}
                rippleColor={Colors.ripple}
                onPress={openModal}
            >
                <Text style={[styles.label, labelStyle]} numberOfLines={1}>
                    {current?.label}
                </Text>
                <Ionicons name='caret-down' style={[styles.icon, iconStyle]} />
            </RectButton>
            <Modal
                visible={visible}
                onRequestClose={closeModal}
                transparent
                statusBarTranslucent
            >
                <Pressable style={styles.backdrop} onPress={closeModal} />
                <View style={[styles.listContainer, layoutStyle]}>
                    <FlatList
                        ref={list}
                        data={items}
                        renderItem={renderItem}
                        keyExtractor={keyExtractor}
                        initialNumToRender={10}
                        contentContainerStyle={styles.contentContainer}
                        showsVerticalScrollIndicator={false}
                        getItemLayout={getItemLayout}
                        initialScrollIndex={current.index}
                    />
                </View>
            </Modal>
        </View>
    );
};

export default Picker;

const styles = StyleSheet.create({
    button: {
        flexDirection: 'row',
        justifyContent: 'space-between',
        alignItems: 'center',
        paddingHorizontal: 15,
        paddingVertical: 10
    },
    label: {
        flex: 1,
        fontSize: 10,
        color: Colors.secondary
    },
    icon: {
        fontSize: 10,
        color: Colors.secondary
    },
    backdrop: {
        flex: 1
    },
    listContainer: {
        borderRadius: 10,
        overflow: 'hidden',
        backgroundColor: Colors.card,
        elevation: 2
    },
    contentContainer: { paddingVertical: 5 },
    item: {
        paddingHorizontal: 10,
        justifyContent: 'center',
        height: ITEM_HEIGHT
    },
    itemLabel: {
        color: Colors.secondary,
        fontSize: 10
    },
    itemActive: { backgroundColor: Colors.primary },
    itemLabelActive: { color: Colors.text }
});

it's kinda customizable, but lot of hard coded styling

saisheelap commented 1 year ago

Facing same issue. any fixes available ?

heinhtetaungg commented 1 year ago

`import React, {FC, ReactElement, useRef, useState} from 'react'; import { FlatList, StyleSheet, Text, TouchableOpacity, Modal, View, FlatListProps, Platform, } from 'react-native'; import theme from '../utils/theme'; import Icon from 'react-native-vector-icons/FontAwesome5'; import InputSearch from './InputSearch';

interface Props { labelField?: string; data: Array; containerStyle?: Object; onSelect: (item: any) => void; placeholder: string; flatProps?: FlatListProps; style?: Object; }

const AutoComplete: FC = ({ data, onSelect, containerStyle = {}, labelField = 'label', style = {}, placeholder, flatProps, }) => { const DropdownButton = useRef(); const [visible, setVisible] = useState(false); const [selected, setSelected] = useState(undefined); const [dropdownTop, setDropdownTop] = useState(0); const [inputValue, setInputValue] = useState(''); const [layoutLoaded, setLayoutLoaded] = useState(false);

const toggleDropdown = (): void => { visible ? setVisible(false) : openDropdown(); };

const openDropdown = (): void => { if (DropdownButton && DropdownButton?.current) { DropdownButton.current?.measure((_fx, _fy, _w, h, _px, py) => { if (!py || !h) { return; } if (Platform.OS === 'ios') { setDropdownTop(py + h); } else { setDropdownTop(py + 20); } }); setVisible(true); setLayoutLoaded(true); } };

const onItemPress = (item): void => { setSelected(item); onSelect(item); setVisible(false); };

const filteredOptions = data.filter((option: any) => // convert to string in case of type error ${option[labelField]} .toLowerCase() ?.includes(${inputValue}.toLowerCase()), );

const renderItem = ({item}): ReactElement<any, any> => ( <TouchableOpacity style={styles.item} onPress={() => onItemPress(item)}>

{item[labelField]}
</TouchableOpacity>

);

const renderDropdown = (): ReactElement<any, any> => { return (

setVisible(false)}> index.toString()} />
);

};

return ( <TouchableOpacity ref={DropdownButton} style={[styles.button, style]} onPress={toggleDropdown}>

{(selected && selected[labelField]) || placeholder}
  <Icon name="angle-down" size={20} />
  {layoutLoaded && renderDropdown()}
</TouchableOpacity>

); };

const styles = StyleSheet.create({ button: { height: 50, flexDirection: 'row', alignItems: 'center', backgroundColor: theme.white, borderRadius: 5, paddingHorizontal: 10, justifyContent: 'space-between', zIndex: 1, }, buttonText: { marginLeft: 5, }, dropdown: { position: 'absolute', backgroundColor: theme.white, width: '100%', shadowColor: '#000000', shadowRadius: 4, shadowOffset: {height: 3, width: 0}, maxHeight: 300, shadowOpacity: 0.5, paddingHorizontal: 10, }, overlay: { width: '100%', height: '100%', }, item: { paddingHorizontal: 10, paddingVertical: 10, //borderBottomWidth: 1, }, });

export default AutoComplete;`

Here is my solution.End up with writing custom but this solution also has issue that is for the first time dropdown picker appear suddenly drop from top but after it appear excepted.if you found the solution for that issue, please point on that.

vaishnaviReyna commented 1 year ago

This is my view hierarchy looks like,

      `<View style={{flexDirection: 'row', flex: 1.2}}>

                <View style={{ flexDirection: 'row', flex: 1, alignItems: 'center', justifyContent: 'center' }}>

                    <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', flex: 0.8, }}>
                        <Text>.....</Text>
                        <TouchableOpacity>
                            <TextInput />
                        </TouchableOpacity>
                    </View>

                    <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', flex: 0.9, }}>
                        <Text>T....</Text>
                        <Dropdown
                            style={{
                                width: 90, borderWidth: 0.5, borderWidth: 1,
                                borderColor: '#0C453A',
                                borderRadius: 10,
                                minHeight: 40,
                                backgroundColor: 'white',
                            }}
                            placeholderStyle={{}}
                            selectedTextStyle={{}}
                            inputSearchStyle={{}}
                            iconStyle={{}}
                            data={tokenArr}
                            search
                            maxHeight={300}
                            minHeight={100}
                            labelField="TokenIdS"
                            valueField="Id"
                            searchField="TokenIdS"
                            placeholder={''}
                            searchPlaceholder="Search..."
                            value={selectedToken?.Id}
                            onFocus={() => setIsFocus(true)}
                            onBlur={() => setIsFocus(false)}
                            onChange={(item) => {
                                //setValue(item.value);
                                setSelectedToken(item)
                                //setIsFocus(false);
                            }}
                        />
                        <TouchableOpacity>
                            <Icon4/>
                        </TouchableOpacity>
                    </View>
                </View>

                <View style={{ flexDirection: 'row', flex: 0.91, alignItems: 'center'}}>

                    <View>
                        <View>
                            <Icon1/>
                            <Text>.....</Text>
                            <Text>.....</Text>
                        </View>
                     <View>
                     </View>
                        <View>
                            <Icon2/>
                            <Text>.....</Text>
                            <Text>.....</Text>
                        </View>
                    </View>
                    <View>
                        <Button>
                            ......
                        </Button>
                    </View>

                </View>

            </View>`

Drop down opens but cannot scroll it. But if i move the dropdown top of the hierarchy, as the first ui component of the parent view, it's working. Anyone know a fix?

"react-native": "0.71.2" "react-native-element-dropdown": "^2.8.0" "react-native-gesture-handler": "^2.9.0"

It's a bug from the package it self. Please install the version <= 2.5.3.

eduardo-santos-tribia commented 1 year ago

I was having a similar issue using the MultiSelect component but for Android only.

I've created a patch with the solution in this related issue.

It might be helpful to solve the issue with the Dropdown component as well.

itallo-S commented 1 year ago

I encountered the same issue when I wrapped the Dropdown component with a view using 'flex-direction: row

Here are two solutions that have worked for me:

1 -Modify the _measure function to include the pageX value:

 const _measure = useCallback(() => {
      if (ref && ref?.current) {
        ref.current.measureInWindow((pageX, pageY, width, height) => {
          const isFull = isTablet
            ? false
            : mode === 'modal' || orientation === 'LANDSCAPE';
          const top = isFull ? 20 : height + pageY + 2;
          const bottom = H - top + height;
          const left = I18nManager.isRTL ? W - width - pageX : pageX;
          setPosition({
            isFull,
            width: Math.floor(width + pageX), // Added pageX
            top: Math.floor(top + statusBarHeight),
            bottom: Math.floor(bottom - statusBarHeight),
            left: Math.floor(left),
            height: Math.floor(height),
          });
        });
      }
    }, [H, W, orientation, mode]);

2 - Modify the _renderModal function to replace the width property with flex: 1:

        <View
      style={StyleSheet.flatten([
        styles.flex1,
        {
          flex: 1, // added
        },
        !isTopPosition
          ? { paddingTop: extendHeight }
          : {
              justifyContent: 'flex-end',
              paddingBottom: extendHeight,
            },
        isFull && styles.fullScreen,
      ])}
    >
ThanSau1105 commented 1 year ago

I encountered the same issue when I wrapped the Dropdown component with a view using 'flex-direction: row

Here are two solutions that have worked for me:

1 -Modify the _measure function to include the pageX value:

const _measure = useCallback(() => {
     if (ref && ref?.current) {
       ref.current.measureInWindow((pageX, pageY, width, height) => {
         const isFull = isTablet
           ? false
           : mode === 'modal' || orientation === 'LANDSCAPE';
         const top = isFull ? 20 : height + pageY + 2;
         const bottom = H - top + height;
         const left = I18nManager.isRTL ? W - width - pageX : pageX;
         setPosition({
           isFull,
           width: Math.floor(width + pageX), // Added pageX
           top: Math.floor(top + statusBarHeight),
           bottom: Math.floor(bottom - statusBarHeight),
           left: Math.floor(left),
           height: Math.floor(height),
         });
       });
     }
   }, [H, W, orientation, mode]);

2 - Modify the _renderModal function to replace the width property with flex: 1:

        <View
      style={StyleSheet.flatten([
        styles.flex1,
        {
          flex: 1, // added
        },
        !isTopPosition
          ? { paddingTop: extendHeight }
          : {
              justifyContent: 'flex-end',
              paddingBottom: extendHeight,
            },
        isFull && styles.fullScreen,
      ])}
    >

Thanks pro!

minh-dai commented 1 year ago

+1

Abhinav-Appleute commented 1 year ago

_renderModal

Are you changing the package itself?

kaylee182000 commented 1 year ago

I encountered the same issue when I wrapped the Dropdown component with a view using 'flex-direction: row Here are two solutions that have worked for me: 1 -Modify the _measure function to include the pageX value:

const _measure = useCallback(() => {
     if (ref && ref?.current) {
       ref.current.measureInWindow((pageX, pageY, width, height) => {
         const isFull = isTablet
           ? false
           : mode === 'modal' || orientation === 'LANDSCAPE';
         const top = isFull ? 20 : height + pageY + 2;
         const bottom = H - top + height;
         const left = I18nManager.isRTL ? W - width - pageX : pageX;
         setPosition({
           isFull,
           width: Math.floor(width + pageX), // Added pageX
           top: Math.floor(top + statusBarHeight),
           bottom: Math.floor(bottom - statusBarHeight),
           left: Math.floor(left),
           height: Math.floor(height),
         });
       });
     }
   }, [H, W, orientation, mode]);

2 - Modify the _renderModal function to replace the width property with flex: 1:

        <View
      style={StyleSheet.flatten([
        styles.flex1,
        {
          flex: 1, // added
        },
        !isTopPosition
          ? { paddingTop: extendHeight }
          : {
              justifyContent: 'flex-end',
              paddingBottom: extendHeight,
            },
        isFull && styles.fullScreen,
      ])}
    >

Thanks pro!

How can you modified it inside node_modules

hoangtrieudeveloper commented 1 year ago

I encountered the same issue when I wrapped the Dropdown component with a view using 'flex-direction: row Here are two solutions that have worked for me: 1 -Modify the _measure function to include the pageX value:

const _measure = useCallback(() => {
     if (ref && ref?.current) {
       ref.current.measureInWindow((pageX, pageY, width, height) => {
         const isFull = isTablet
           ? false
           : mode === 'modal' || orientation === 'LANDSCAPE';
         const top = isFull ? 20 : height + pageY + 2;
         const bottom = H - top + height;
         const left = I18nManager.isRTL ? W - width - pageX : pageX;
         setPosition({
           isFull,
           width: Math.floor(width + pageX), // Added pageX
           top: Math.floor(top + statusBarHeight),
           bottom: Math.floor(bottom - statusBarHeight),
           left: Math.floor(left),
           height: Math.floor(height),
         });
       });
     }
   }, [H, W, orientation, mode]);

2 - Modify the _renderModal function to replace the width property with flex: 1:

        <View
      style={StyleSheet.flatten([
        styles.flex1,
        {
          flex: 1, // added
        },
        !isTopPosition
          ? { paddingTop: extendHeight }
          : {
              justifyContent: 'flex-end',
              paddingBottom: extendHeight,
            },
        isFull && styles.fullScreen,
      ])}
    >

Thanks pro!

How can you modified it inside node_modules

Screenshot 2023-07-21 at 09 41 03

Bạn bấm ctrl rồi click vào Dropdown thì sẽ đến component của Dropdown thì bạn tìm đến đoạn được note ở trên để sửa

sutgeorge commented 6 months ago

I encountered the same issue when I wrapped the Dropdown component with a view using 'flex-direction: row

Here are two solutions that have worked for me:

1 -Modify the _measure function to include the pageX value:

const _measure = useCallback(() => {
     if (ref && ref?.current) {
       ref.current.measureInWindow((pageX, pageY, width, height) => {
         const isFull = isTablet
           ? false
           : mode === 'modal' || orientation === 'LANDSCAPE';
         const top = isFull ? 20 : height + pageY + 2;
         const bottom = H - top + height;
         const left = I18nManager.isRTL ? W - width - pageX : pageX;
         setPosition({
           isFull,
           width: Math.floor(width + pageX), // Added pageX
           top: Math.floor(top + statusBarHeight),
           bottom: Math.floor(bottom - statusBarHeight),
           left: Math.floor(left),
           height: Math.floor(height),
         });
       });
     }
   }, [H, W, orientation, mode]);

2 - Modify the _renderModal function to replace the width property with flex: 1:

        <View
      style={StyleSheet.flatten([
        styles.flex1,
        {
          flex: 1, // added
        },
        !isTopPosition
          ? { paddingTop: extendHeight }
          : {
              justifyContent: 'flex-end',
              paddingBottom: extendHeight,
            },
        isFull && styles.fullScreen,
      ])}
    >

This is definitely not maintainable in the long-term, but an interesting solution nonetheless.