Fix drag and drop in dropdown (#16622)

## Bug description

Due to the dropdown using floating ui, there was an offset on the
draggable item when the drag and drop was implemented inside a
scrollable dropdown.

## Video QA

### Before


https://github.com/user-attachments/assets/fe4b4c36-39ae-4d26-9e19-85d5ffd23b30


### After


https://github.com/user-attachments/assets/1f00b9b0-c231-49cb-a232-75ca42a98e8c
This commit is contained in:
Raphaël Bosi
2025-12-17 14:41:37 +01:00
committed by GitHub
parent 967298fe09
commit ca976afa10
5 changed files with 52 additions and 16 deletions
@@ -1,6 +1,7 @@
import { useTheme } from '@emotion/react';
import { Draggable } from '@hello-pangea/dnd';
import { isFunction } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
type DraggableItemProps = {
draggableId: string;
@@ -12,6 +13,7 @@ type DraggableItemProps = {
isInsideScrollableContainer?: boolean;
draggableComponentStyles?: React.CSSProperties;
disableDraggingBackground?: boolean;
containerOffsetY?: number;
};
export const DraggableItem = ({
@@ -22,6 +24,7 @@ export const DraggableItem = ({
isInsideScrollableContainer,
draggableComponentStyles,
disableDraggingBackground,
containerOffsetY,
}: DraggableItemProps) => {
const theme = useTheme();
@@ -47,11 +50,15 @@ export const DraggableItem = ({
...draggableComponentStyles,
...draggableStyle,
left: 'auto',
...(isInsideScrollableContainer ? {} : { top: 'auto' }),
transform: draggableStyle?.transform?.replace(
/\(-?\d+px,/,
'(0,',
),
...(isInsideScrollableContainer
? {
top: `${(isDefined(draggableStyle) && 'top' in draggableStyle ? draggableStyle.top : 0) - (containerOffsetY ?? 0)}px`,
}
: { top: 'auto' }),
background:
!disableDraggingBackground && isDragging
? theme.background.transparent.light
@@ -9,6 +9,7 @@ import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/
import { useToggleDropdown } from '@/ui/layout/dropdown/hooks/useToggleDropdown';
import { dropdownMaxHeightComponentState } from '@/ui/layout/dropdown/states/internal/dropdownMaxHeightComponentState';
import { dropdownMaxWidthComponentState } from '@/ui/layout/dropdown/states/internal/dropdownMaxWidthComponentState';
import { dropdownYPositionComponentState } from '@/ui/layout/dropdown/states/internal/dropdownYPositionComponentState';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { type DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset';
import { type GlobalHotkeysConfig } from '@/ui/utilities/hotkey/types/GlobalHotkeysConfig';
@@ -117,6 +118,11 @@ export const Dropdown = ({
dropdownId,
);
const setDropdownYPosition = useSetRecoilComponentState(
dropdownYPositionComponentState,
dropdownId,
);
const isMobile = useIsMobile();
const bottomAutoresizePadding = isMobile
? (middlewareBoundaryPadding.bottomMobile ??
@@ -143,7 +149,7 @@ export const Dropdown = ({
...boundaryOptions,
}),
size({
apply: ({ availableHeight, availableWidth }) => {
apply: ({ availableHeight, availableWidth, y: floatingY }) => {
flushSync(() => {
const maxHeightToApply =
availableHeight < DROPDOWN_RESIZE_MIN_HEIGHT
@@ -157,6 +163,7 @@ export const Dropdown = ({
setDropdownMaxHeight(maxHeightToApply);
setDropdownMaxWidth(maxWidthToApply);
setDropdownYPosition(floatingY);
});
},
...boundaryOptions,
@@ -0,0 +1,10 @@
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const dropdownYPositionComponentState = createComponentState<
number | undefined
>({
key: 'dropdownYPositionComponentState',
componentInstanceContext: DropdownComponentInstanceContext,
defaultValue: undefined,
});