0

So I created a custom autocomplete in React with Tailwind and flowbite, and and and it works like it must, except that when i use the autocomplete in a flowbite modal and the modal isn't big, the moment i open the select box, it' will only appear inside the modal, so the rest won't be shown tried different things but with no result.

this is the normal modal with the autocomplete not open enter image description here

this is after i open the automplete box just scrolled a little bit it show you more

enter image description here

this is the autocomplete code

/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
/* eslint-disable no-underscore-dangle */
import React, { useEffect, useRef, useState } from 'react';
import { AiFillCaretDown, AiFillCaretUp } from 'react-icons/ai';
import { MdOutlineCancel } from 'react-icons/md';
import { IAutoComplete } from './autoComplete.types';

/**
 * 
 * @param : this autocomplete take an array of objects as options for now
 * @example : 
 *   const areiba = [
    { id: 0, name: 'jka' },
    { id: 1, name: 'jazeka' },
];
 * 
 */

const autoComplete: React.FC<IAutoComplete> = ({
    onChange,
    options,
    getOptionLabel,
    placeholder,
    key,
    name,
    styles,
}) => {
    const [selectedOption, setSelectedOption] = useState('');
    const [showOptions, setShowOptions] = useState(false);
    const [cursor, setCursor] = useState(-1);
    const [cancel, setCancel] = useState(false);

    const ref = useRef<any>();

    const select = (selectedValue: string) => {
        setSelectedOption(selectedValue);
        setShowOptions(false);
    };

    const handleChange = (text: string) => {
        setSelectedOption(text);
        if (text.trim().length !== 0) {
            setCancel(true);
        } else {
            setCancel(false);
            onChange({});
        }

        setCursor(-1);
        if (!showOptions) {
            setShowOptions(true);
        }
    };

    const filteredOptions = options.filter((option) =>
        getOptionLabel(option).toLocaleLowerCase().includes(selectedOption),
    );

    // const moveCursorDown = () => {
    //     if (cursor < filteredOptions.length - 1) {
    //         setCursor((c) => c + 1);
    //     }
    // };

    // const moveCursorUp = () => {
    //     if (cursor > 0) {
    //         setCursor((c) => c - 1);
    //     }
    // };

    // const handleNav = (e: React.KeyboardEvent<HTMLInputElement>) => {
    //     // eslint-disable-next-line default-case
    //     switch (e.key) {
    //         case 'ArrowUp':
    //             moveCursorUp();
    //             break;
    //         case 'ArrowDown':
    //             moveCursorDown();
    //             break;
    //         case 'Enter':
    //             if (cursor >= 0 && cursor < filteredOptions.length) {
    //                 // console.log(filteredOptions[cursor], 'filter cursor');
    //                 // select(filteredOptions[cursor]);
    //             }
    //             break;
    //     }
    // };

    useEffect(() => {
        const handleClick = (e: MouseEvent) => {
            if (ref.current && !ref.current.contains(e.target)) {
                setShowOptions(false);
            }
        };

        document.addEventListener('click', handleClick);

        return () => {
            document.removeEventListener('click', handleClick);
        };
    }, [ref]);

    const getClassname = (i: number, arr: any[]) => {
        let className = 'px-4 hover:bg-gray-100 hover:dark:bg-[#333a47] ';

        if (i === 0 && arr.length === 1) {
            className += 'py-2 rounded-lg ';
        } else if (i === arr.length - 1) {
            className += 'pt-1 pb-2 rounded-b-lg ';
        } else if (i === 0) {
            className += ' pt-2 pb-1 rounded-t-lg ';
        } else {
            className += 'py-1';
        }

        if (cursor === i) {
            className += ' bg-gray-100 dark:bg-[#333a47]';
        }

        return className;
    };

    return (
        <>
            <div className="relative">
                <div className="relative">
                    <input
                        style={styles}
                        name={name}
                        ref={ref}
                        value={selectedOption}
                        onChange={(e) => {
                            handleChange(e.target.value);
                        }}
                        onFocus={(e) => {
                            setShowOptions(false);
                            if (e.target.value.trim().length > 0) {
                                setCancel(true);
                            }
                        }}
                        autoComplete="off"
                        type="text"
                        id="autoComplete"
                        className="block w-full p-2.5 pl-3 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
                        placeholder={placeholder}
                        onClick={() => {
                            setShowOptions(!showOptions);
                        }}
                    />
                    {cancel && (
                        <button
                            type="submit"
                            onClick={() => {
                                setSelectedOption('');
                                setCancel(false);
                                onChange({});
                            }}
                            className="text-white absolute right-8 inset-y-2 px-1 hover:bg-tertiaryBgBtn rounded-3xl text-sm dark:focus:ring-blue-800"
                        >
                            <MdOutlineCancel className="h-5 w-5 text-black dark:text-white" />
                        </button>
                    )}

                    <div className="absolute inset-y-0 right-3 flex items-center pl-3 pointer-events-none">
                        {showOptions ? <AiFillCaretUp className="h-4 w-4" /> : <AiFillCaretDown className="h-4 w-4" />}
                    </div>

                    {/* Autocomplete dropdown */}
                </div>
                <ul
                    className={`absolute  w-full rounded-lg shadow-lg bg-background-primary max-h-52 overflow-auto z-[50] ${
                        !showOptions && 'hidden'
                    } select-none`}
                >
                    {filteredOptions.length > 0 ? (
                        filteredOptions.map((option, i: number, arr: any) => {
                            return (
                                <li
                                    className={getClassname(i, arr)}
                                    key={option[key]}
                                    onClick={() => {
                                        onChange(option);
                                        select(getOptionLabel(option));
                                    }}
                                >
                                    {getOptionLabel(option)}
                                </li>
                            );
                        })
                    ) : (
                        <li className="px-4 py-2 text-gray-500">No results</li>
                    )}
                </ul>
            </div>
        </>
    );
};

export default autoComplete;


and this is the modal

<Modal dismissible show={openAssignModal} popup onClose={() => setOpenAssignModal(false)} size="xl">
            <Modal.Header>Assign Orders to a call agent</Modal.Header>
            <Modal.Body className="!p-2 ">
                {callAgents && (
                    <div className="flex flex-col items-center justify-between p-2 space-y-3 md:flex-row md:space-y-0 md:space-x-4">
                        <div className="grow ">
                            <AutoComplete
                                key="id"
                                placeholder="Call Agent"
                                options={callAgents as []}
                                getOptionLabel={(option: any) => `${option.firstName} ${option.lastName}`}
                                // eslint-disable-next-line no-underscore-dangle
                                onChange={(option) => setCallAgent(option._id as string)}
                            />
                        </div>
                        <div className="">
                            <CustomBtn
                                variant="primary"
                                onClick={() => {
                                    assignOrderCallAgent();
                                }}
                            >
                                <BsClipboardCheckFill className="mr-2 h-5 w-5" />
                                Assign
                            </CustomBtn>
                        </div>
                    </div>
                )}
            </Modal.Body>
        </Modal>

I know it's something in the first div with relative but don't know what should be changed to exactly.

Thank you.

Aymen khlil
  • 25
  • 1
  • 4

0 Answers0