Fixed many dropdown bugs (#8256)
Many dropdown bugs have been fixed, more refactoring is needed. Dropdown fixed : - Filter select - Sort select - Visible field select - Hidden field select - Multi item picker (phones, links, emails, etc.) - Phone country select
This commit is contained in:
@ -74,8 +74,8 @@ export const PhoneCountryPickerDropdownButton = ({
|
||||
);
|
||||
|
||||
const handleChange = (countryCode: string) => {
|
||||
onChange(countryCode);
|
||||
closeDropdown();
|
||||
onChange(countryCode);
|
||||
};
|
||||
|
||||
const countries = useCountries();
|
||||
@ -89,7 +89,6 @@ export const PhoneCountryPickerDropdownButton = ({
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
dropdownMenuWidth={'100%'}
|
||||
dropdownId="country-picker-dropdown-id"
|
||||
dropdownHotkeyScope={{ scope: CountryPickerHotkeyScope.CountryPicker }}
|
||||
clickableComponent={
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
size,
|
||||
useFloating,
|
||||
} from '@floating-ui/react';
|
||||
import { MouseEvent, useEffect, useRef } from 'react';
|
||||
import { MouseEvent, ReactNode, useRef } from 'react';
|
||||
import { Keys } from 'react-hotkeys-hook';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
@ -27,8 +27,8 @@ import { DropdownOnToggleEffect } from './DropdownOnToggleEffect';
|
||||
|
||||
type DropdownProps = {
|
||||
className?: string;
|
||||
clickableComponent?: JSX.Element | JSX.Element[];
|
||||
dropdownComponents: JSX.Element | JSX.Element[];
|
||||
clickableComponent?: ReactNode;
|
||||
dropdownComponents: ReactNode;
|
||||
hotkey?: {
|
||||
key: Keys;
|
||||
scope: string;
|
||||
@ -65,13 +65,8 @@ export const Dropdown = ({
|
||||
}: DropdownProps) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const {
|
||||
isDropdownOpen,
|
||||
toggleDropdown,
|
||||
closeDropdown,
|
||||
dropdownWidth,
|
||||
setDropdownPlacement,
|
||||
} = useDropdown(dropdownId);
|
||||
const { isDropdownOpen, toggleDropdown, closeDropdown, dropdownWidth } =
|
||||
useDropdown(dropdownId);
|
||||
|
||||
const offsetMiddlewares = [];
|
||||
|
||||
@ -83,18 +78,21 @@ export const Dropdown = ({
|
||||
offsetMiddlewares.push(offset({ mainAxis: dropdownOffset.y }));
|
||||
}
|
||||
|
||||
const { refs, floatingStyles, placement } = useFloating({
|
||||
const { refs, floatingStyles } = useFloating({
|
||||
placement: dropdownPlacement,
|
||||
middleware: [
|
||||
flip(),
|
||||
size({
|
||||
padding: 12 + 20, // 12px for padding bottom, 20px for dropdown bottom margin target
|
||||
padding: 32,
|
||||
apply: ({ availableHeight, elements }) => {
|
||||
elements.floating.style.maxHeight =
|
||||
availableHeight >= elements.floating.scrollHeight
|
||||
? ''
|
||||
: `${availableHeight}px`;
|
||||
|
||||
elements.floating.style.height = 'auto';
|
||||
},
|
||||
boundary: document.querySelector('#root') ?? undefined,
|
||||
}),
|
||||
...offsetMiddlewares,
|
||||
],
|
||||
@ -102,10 +100,6 @@ export const Dropdown = ({
|
||||
strategy: dropdownStrategy,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setDropdownPlacement(placement);
|
||||
}, [placement, setDropdownPlacement]);
|
||||
|
||||
const handleHotkeyTriggered = () => {
|
||||
toggleDropdown();
|
||||
};
|
||||
|
||||
@ -23,10 +23,10 @@ const StyledDropdownMenu = styled.div<{
|
||||
|
||||
display: flex;
|
||||
|
||||
height: 100%;
|
||||
|
||||
flex-direction: column;
|
||||
z-index: 30;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
width: ${({ width = 160 }) =>
|
||||
typeof width === 'number' ? `${width}px` : width};
|
||||
`;
|
||||
|
||||
@ -13,7 +13,6 @@ const StyledDropdownMenuItemsExternalContainer = styled.div<{
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
max-height: ${({ hasMaxHeight }) => (hasMaxHeight ? '188px' : 'none')};
|
||||
overflow-y: auto;
|
||||
|
||||
padding: var(--padding);
|
||||
|
||||
@ -34,6 +33,8 @@ const StyledDropdownMenuItemsInternalContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
// TODO: refactor this, the dropdown should handle the max height behavior + scroll with the size middleware
|
||||
// We should instead create a DropdownMenuItemsContainerScrollable or take for granted that it is the default behavior
|
||||
export const DropdownMenuItemsContainer = ({
|
||||
children,
|
||||
hasMaxHeight,
|
||||
|
||||
@ -0,0 +1,105 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { FunctionComponent, MouseEvent, ReactElement, ReactNode } from 'react';
|
||||
import {
|
||||
IconChevronRight,
|
||||
IconComponent,
|
||||
IconDotsVertical,
|
||||
LightIconButton,
|
||||
LightIconButtonProps,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import { SelectHotkeyScope } from '@/ui/input/types/SelectHotkeyScope';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
|
||||
import {
|
||||
StyledHoverableMenuItemBase,
|
||||
StyledMenuItemLeftContent,
|
||||
} from '../internals/components/StyledMenuItemBase';
|
||||
import { MenuItemAccent } from '../types/MenuItemAccent';
|
||||
|
||||
export type MenuItemIconButton = {
|
||||
Wrapper?: FunctionComponent<{ iconButton: ReactElement }>;
|
||||
Icon: IconComponent;
|
||||
accent?: LightIconButtonProps['accent'];
|
||||
onClick?: (event: MouseEvent<any>) => void;
|
||||
};
|
||||
|
||||
export type MenuItemWithOptionDropdownProps = {
|
||||
accent?: MenuItemAccent;
|
||||
className?: string;
|
||||
dropdownContent: ReactNode;
|
||||
dropdownId: string;
|
||||
isIconDisplayedOnHoverOnly?: boolean;
|
||||
isTooltipOpen?: boolean;
|
||||
LeftIcon?: IconComponent | null;
|
||||
RightIcon?: IconComponent | null;
|
||||
onClick?: (event: MouseEvent<HTMLDivElement>) => void;
|
||||
onMouseEnter?: (event: MouseEvent<HTMLDivElement>) => void;
|
||||
onMouseLeave?: (event: MouseEvent<HTMLDivElement>) => void;
|
||||
testId?: string;
|
||||
text: ReactNode;
|
||||
hasSubMenu?: boolean;
|
||||
};
|
||||
|
||||
// TODO: refactor this
|
||||
export const MenuItemWithOptionDropdown = ({
|
||||
accent = 'default',
|
||||
className,
|
||||
isIconDisplayedOnHoverOnly = true,
|
||||
dropdownContent,
|
||||
dropdownId,
|
||||
LeftIcon,
|
||||
RightIcon,
|
||||
onClick,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
testId,
|
||||
text,
|
||||
hasSubMenu = false,
|
||||
}: MenuItemWithOptionDropdownProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const handleMenuItemClick = (event: MouseEvent<HTMLDivElement>) => {
|
||||
if (!onClick) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
onClick?.(event);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledHoverableMenuItemBase
|
||||
data-testid={testId ?? undefined}
|
||||
onClick={handleMenuItemClick}
|
||||
className={className}
|
||||
accent={accent}
|
||||
isIconDisplayedOnHoverOnly={isIconDisplayedOnHoverOnly}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
<StyledMenuItemLeftContent>
|
||||
<MenuItemLeftContent LeftIcon={LeftIcon ?? undefined} text={text} />
|
||||
</StyledMenuItemLeftContent>
|
||||
<div className="hoverable-buttons">
|
||||
<Dropdown
|
||||
clickableComponent={
|
||||
<LightIconButton
|
||||
Icon={RightIcon ?? IconDotsVertical}
|
||||
size="small"
|
||||
/>
|
||||
}
|
||||
dropdownComponents={dropdownContent}
|
||||
dropdownId={dropdownId}
|
||||
dropdownHotkeyScope={{ scope: SelectHotkeyScope.Select }}
|
||||
disableBlur
|
||||
/>
|
||||
</div>
|
||||
{hasSubMenu && (
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.sm}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
)}
|
||||
</StyledHoverableMenuItemBase>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user