import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { autoUpdate, flip, offset, size, useFloating, } from '@floating-ui/react'; import React, { useCallback, useId, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { ReadonlyDeep } from 'type-fest'; import { useDebouncedCallback } from 'use-debounce'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useLingui } from '@lingui/react/macro'; import { useUpdateEffect } from '~/hooks/useUpdateEffect'; import { AppTooltip } from 'twenty-ui/display'; import { MenuItem, MenuItemSelect } from 'twenty-ui/navigation'; import { SelectOption } from 'twenty-ui/input'; const StyledFloatingDropdown = styled.div` z-index: ${({ theme }) => theme.lastLayerZIndex}; `; interface MatchColumnSelectProps { onChange: (value: ReadonlyDeep | null) => void; value?: ReadonlyDeep; options: readonly ReadonlyDeep[]; placeholder?: string; name?: string; } export const MatchColumnSelect = ({ onChange, value, options: initialOptions, placeholder, }: MatchColumnSelectProps) => { const theme = useTheme(); const idPrefix = useId(); const dropdownContainerRef = useRef(null); const [isOpen, setIsOpen] = useState(false); const [searchFilter, setSearchFilter] = useState(''); const [options, setOptions] = useState(initialOptions); const { refs, floatingStyles } = useFloating({ strategy: 'absolute', middleware: [ offset(() => { return parseInt(theme.spacing(2), 10); }), flip(), size(), ], whileElementsMounted: autoUpdate, open: isOpen, placement: 'bottom-start', }); const handleSearchFilterChange = useCallback( (text: string) => { setOptions( initialOptions.filter((option) => option.label.toLowerCase().includes(text.toLowerCase()), ), ); }, [initialOptions], ); const debouncedHandleSearchFilter = useDebouncedCallback( handleSearchFilterChange, 100, { leading: true, }, ); const handleFilterChange = (event: React.ChangeEvent) => { const value = event.currentTarget.value; setSearchFilter(value); debouncedHandleSearchFilter(value); }; const handleDropdownItemClick = () => { setIsOpen(true); }; const handleChange = (option: ReadonlyDeep) => { onChange(option); setIsOpen(false); }; useListenClickOutside({ refs: [dropdownContainerRef], callback: () => { setIsOpen(false); }, listenerId: 'match-column-select', }); useUpdateEffect(() => { setOptions(initialOptions); }, [initialOptions]); const { t } = useLingui(); return ( <>
{isOpen && createPortal( {options?.map((option, index) => { const id = `${idPrefix}-option-${index}`; return (
handleChange(option)} disabled={ option.disabled && value?.value !== option.value } LeftIcon={option?.Icon} text={option.label} />
{option.disabled && value?.value !== option.value && createPortal( , document.body, )}
); })} {options?.length === 0 && ( )}
, document.body, )} ); };